Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 6fa2ead

Browse files
author
Chris Yang
authored
[google_maps_flutter_platform_interface] add custom tile support (#3418)
1 parent c923ef8 commit 6fa2ead

File tree

15 files changed

+724
-4
lines changed

15 files changed

+724
-4
lines changed

packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.2.0
2+
3+
* Add TileOverlay support.
4+
15
## 1.1.0
26

37
* Add support for holes in Polygons.

packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import 'package:flutter/gestures.dart';
1212

1313
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
1414
import 'package:stream_transform/stream_transform.dart';
15+
import '../types/tile_overlay_updates.dart';
16+
import '../types/utils/tile_overlay.dart';
1517

1618
/// An implementation of [GoogleMapsFlutterPlatform] that uses [MethodChannel] to communicate with the native code.
1719
///
@@ -33,6 +35,9 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform {
3335
return _channels[mapId];
3436
}
3537

38+
// Keep a collection of mapId to a map of TileOverlays.
39+
final Map<int, Map<TileOverlayId, TileOverlay>> _tileOverlays = {};
40+
3641
/// Initializes the platform interface with [id].
3742
///
3843
/// This method is called when the plugin is first initialized.
@@ -184,6 +189,18 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform {
184189
LatLng.fromJson(call.arguments['position']),
185190
));
186191
break;
192+
case 'tileOverlay#getTile':
193+
final Map<TileOverlayId, TileOverlay> tileOverlaysForThisMap =
194+
_tileOverlays[mapId];
195+
final String tileOverlayId = call.arguments['tileOverlayId'];
196+
final TileOverlay tileOverlay = tileOverlaysForThisMap[tileOverlayId];
197+
assert(tileOverlay.tileProvider.getTile != null);
198+
final Tile tile = await tileOverlay.tileProvider.getTile(
199+
call.arguments['x'],
200+
call.arguments['y'],
201+
call.arguments['zoom'],
202+
);
203+
return tile.toJson();
187204
default:
188205
throw MissingPluginException();
189206
}
@@ -281,6 +298,50 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform {
281298
);
282299
}
283300

301+
/// Updates tile overlay configuration.
302+
///
303+
/// Change listeners are notified once the update has been made on the
304+
/// platform side.
305+
///
306+
/// The returned [Future] completes after listeners have been notified.
307+
///
308+
/// If `newTileOverlays` is null, all the [TileOverlays] are removed for the Map with `mapId`.
309+
@override
310+
Future<void> updateTileOverlays({
311+
Set<TileOverlay> newTileOverlays,
312+
@required int mapId,
313+
}) {
314+
final Map<TileOverlayId, TileOverlay> currentTileOverlays =
315+
_tileOverlays[mapId];
316+
Set<TileOverlay> previousSet =
317+
currentTileOverlays != null ? currentTileOverlays.values.toSet() : null;
318+
final TileOverlayUpdates updates =
319+
TileOverlayUpdates.from(previousSet, newTileOverlays);
320+
_tileOverlays[mapId] = keyTileOverlayId(newTileOverlays);
321+
return channel(mapId).invokeMethod<void>(
322+
'tileOverlays#update',
323+
updates.toJson(),
324+
);
325+
}
326+
327+
/// Clears the tile cache so that all tiles will be requested again from the
328+
/// [TileProvider].
329+
///
330+
/// The current tiles from this tile overlay will also be
331+
/// cleared from the map after calling this method. The Google Map SDK maintains a small
332+
/// in-memory cache of tiles. If you want to cache tiles for longer, you
333+
/// should implement an on-disk cache.
334+
@override
335+
Future<void> clearTileCache(
336+
TileOverlayId tileOverlayId, {
337+
@required int mapId,
338+
}) {
339+
return channel(mapId)
340+
.invokeMethod<void>('tileOverlays#clearTileCache', <String, dynamic>{
341+
'tileOverlayId': tileOverlayId.value,
342+
});
343+
}
344+
284345
/// Starts an animated change of the map camera position.
285346
///
286347
/// The returned [Future] completes after the change has been started on the

packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,33 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface {
115115
throw UnimplementedError('updateCircles() has not been implemented.');
116116
}
117117

118+
/// Updates tile overlay configuration.
119+
///
120+
/// Change listeners are notified once the update has been made on the
121+
/// platform side.
122+
///
123+
/// The returned [Future] completes after listeners have been notified.
124+
Future<void> updateTileOverlays({
125+
Set<TileOverlay> newTileOverlays,
126+
@required int mapId,
127+
}) {
128+
throw UnimplementedError('updateTileOverlays() has not been implemented.');
129+
}
130+
131+
/// Clears the tile cache so that all tiles will be requested again from the
132+
/// [TileProvider].
133+
///
134+
/// The current tiles from this tile overlay will also be
135+
/// cleared from the map after calling this method. The Google Maps SDK maintains a small
136+
/// in-memory cache of tiles. If you want to cache tiles for longer, you
137+
/// should implement an on-disk cache.
138+
Future<void> clearTileCache(
139+
TileOverlayId tileOverlayId, {
140+
@required int mapId,
141+
}) {
142+
throw UnimplementedError('clearTileCache() has not been implemented.');
143+
}
144+
118145
/// Starts an animated change of the map camera position.
119146
///
120147
/// The returned [Future] completes after the change has been started on the
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:typed_data';
6+
import 'package:meta/meta.dart' show immutable;
7+
8+
/// Contains information about a Tile that is returned by a [TileProvider].
9+
@immutable
10+
class Tile {
11+
/// Creates an immutable representation of a [Tile] to draw by [TileProvider].
12+
const Tile(this.width, this.height, this.data);
13+
14+
/// The width of the image encoded by data in logical pixels.
15+
final int width;
16+
17+
/// The height of the image encoded by data in logical pixels.
18+
final int height;
19+
20+
/// A byte array containing the image data.
21+
///
22+
/// The image data format must be natively supported for decoding by the platform.
23+
/// e.g on Android it can only be one of the [supported image formats for decoding](https://developer.android.com/guide/topics/media/media-formats#image-formats).
24+
final Uint8List data;
25+
26+
/// Converts this object to JSON.
27+
Map<String, dynamic> toJson() {
28+
final Map<String, dynamic> json = <String, dynamic>{};
29+
30+
void addIfPresent(String fieldName, dynamic value) {
31+
if (value != null) {
32+
json[fieldName] = value;
33+
}
34+
}
35+
36+
addIfPresent('width', width);
37+
addIfPresent('height', height);
38+
addIfPresent('data', data);
39+
40+
return json;
41+
}
42+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:ui' show hashValues;
6+
import 'package:flutter/foundation.dart';
7+
8+
import 'types.dart';
9+
import 'package:meta/meta.dart' show immutable, required;
10+
11+
/// Uniquely identifies a [TileOverlay] among [GoogleMap] tile overlays.
12+
@immutable
13+
class TileOverlayId {
14+
/// Creates an immutable identifier for a [TileOverlay].
15+
TileOverlayId(this.value) : assert(value != null);
16+
17+
/// The value of the [TileOverlayId].
18+
final String value;
19+
20+
@override
21+
bool operator ==(Object other) {
22+
if (other.runtimeType != runtimeType) {
23+
return false;
24+
}
25+
return other is TileOverlayId && other.value == value;
26+
}
27+
28+
@override
29+
int get hashCode => value.hashCode;
30+
31+
@override
32+
String toString() => '${objectRuntimeType(this, 'TileOverlayId')}($value)';
33+
}
34+
35+
/// A set of images which are displayed on top of the base map tiles.
36+
///
37+
/// These tiles may be transparent, allowing you to add features to existing maps.
38+
///
39+
/// ## Tile Coordinates
40+
///
41+
/// Note that the world is projected using the Mercator projection
42+
/// (see [Wikipedia](https://en.wikipedia.org/wiki/Mercator_projection)) with the left (west) side
43+
/// of the map corresponding to -180 degrees of longitude and the right (east) side of the map
44+
/// corresponding to 180 degrees of longitude. To make the map square, the top (north) side of the
45+
/// map corresponds to 85.0511 degrees of latitude and the bottom (south) side of the map
46+
/// corresponds to -85.0511 degrees of latitude. Areas outside this latitude range are not rendered.
47+
///
48+
/// At each zoom level, the map is divided into tiles and only the tiles that overlap the screen are
49+
/// downloaded and rendered. Each tile is square and the map is divided into tiles as follows:
50+
///
51+
/// * At zoom level 0, one tile represents the entire world. The coordinates of that tile are
52+
/// (x, y) = (0, 0).
53+
/// * At zoom level 1, the world is divided into 4 tiles arranged in a 2 x 2 grid.
54+
/// * ...
55+
/// * At zoom level N, the world is divided into 4N tiles arranged in a 2N x 2N grid.
56+
///
57+
/// Note that the minimum zoom level that the camera supports (which can depend on various factors)
58+
/// is GoogleMap.getMinZoomLevel and the maximum zoom level is GoogleMap.getMaxZoomLevel.
59+
///
60+
/// The coordinates of the tiles are measured from the top left (northwest) corner of the map.
61+
/// At zoom level N, the x values of the tile coordinates range from 0 to 2N - 1 and increase from
62+
/// west to east and the y values range from 0 to 2N - 1 and increase from north to south.
63+
///
64+
class TileOverlay {
65+
/// Creates an immutable representation of a [TileOverlay] to draw on [GoogleMap].
66+
const TileOverlay({
67+
@required this.tileOverlayId,
68+
this.fadeIn = true,
69+
this.tileProvider,
70+
this.transparency = 0.0,
71+
this.zIndex,
72+
this.visible = true,
73+
this.tileSize = 256,
74+
}) : assert(transparency >= 0.0 && transparency <= 1.0);
75+
76+
/// Uniquely identifies a [TileOverlay].
77+
final TileOverlayId tileOverlayId;
78+
79+
/// Whether the tiles should fade in. The default is true.
80+
final bool fadeIn;
81+
82+
/// The tile provider to use for this tile overlay.
83+
final TileProvider tileProvider;
84+
85+
/// The transparency of the tile overlay. The default transparency is 0 (opaque).
86+
final double transparency;
87+
88+
/// The tile overlay's zIndex, i.e., the order in which it will be drawn where
89+
/// overlays with larger values are drawn above those with lower values
90+
final int zIndex;
91+
92+
/// The visibility for the tile overlay. The default visibility is true.
93+
final bool visible;
94+
95+
/// Specifies the number of logical pixels (not points) that the returned tile images will prefer
96+
/// to display as. iOS only.
97+
///
98+
/// Defaults to 256, which is the traditional size of Google Maps tiles.
99+
/// As an example, an application developer may wish to provide retina tiles (512 pixel edge length)
100+
/// on retina devices, to keep the same number of tiles per view as the default value of 256
101+
/// would give on a non-retina device.
102+
final int tileSize;
103+
104+
/// Creates a new [TileOverlay] object whose values are the same as this instance,
105+
/// unless overwritten by the specified parameters.
106+
TileOverlay copyWith({
107+
TileOverlayId tileOverlayId,
108+
bool fadeInParam,
109+
double transparencyParam,
110+
int zIndexParam,
111+
bool visibleParam,
112+
int tileSizeParam,
113+
}) {
114+
return TileOverlay(
115+
tileOverlayId: tileOverlayId,
116+
fadeIn: fadeInParam ?? fadeIn,
117+
transparency: transparencyParam ?? transparency,
118+
zIndex: zIndexParam ?? zIndex,
119+
visible: visibleParam ?? visible,
120+
tileSize: tileSizeParam ?? tileSize,
121+
);
122+
}
123+
124+
/// Converts this object to JSON.
125+
Map<String, dynamic> toJson() {
126+
final Map<String, dynamic> json = <String, dynamic>{};
127+
128+
void addIfPresent(String fieldName, dynamic value) {
129+
if (value != null) {
130+
json[fieldName] = value;
131+
}
132+
}
133+
134+
addIfPresent('tileOverlayId', tileOverlayId.value);
135+
addIfPresent('fadeIn', fadeIn);
136+
addIfPresent('transparency', transparency);
137+
addIfPresent('zIndex', zIndex);
138+
addIfPresent('visible', visible);
139+
addIfPresent('tileSize', tileSize);
140+
141+
return json;
142+
}
143+
144+
@override
145+
bool operator ==(Object other) {
146+
if (other.runtimeType != runtimeType) {
147+
return false;
148+
}
149+
return other is TileOverlay &&
150+
tileOverlayId == other.tileOverlayId &&
151+
fadeIn == other.fadeIn &&
152+
transparency == other.transparency &&
153+
zIndex == other.zIndex &&
154+
visible == other.visible &&
155+
tileSize == other.tileSize;
156+
}
157+
158+
@override
159+
int get hashCode => hashValues(
160+
tileOverlayId, fadeIn, transparency, zIndex, visible, tileSize);
161+
}

0 commit comments

Comments
 (0)