-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[google_maps_flutter_web] Initial support for custom overlays #3538
Conversation
5e536d5
to
026f4e4
Compare
packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart
Outdated
Show resolved
Hide resolved
expect(capturedPolylines.first.polylineId.value, 'polyline-1'); | ||
}); | ||
|
||
testWidgets('empty infoWindow does not create InfoWindow instance.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original comment by @AsturaPhoenix:
Leaving a comment here because it wasn't obvious to me the second time I looked at it:
This test was removed because it became trivial once using Mockito verification; it essentially tests to make sure that Marker.infoWindow
defaults to InfoWindow.noText
. On the other hand, the argument could be made to keep it anyway (e.g. to construct the verification with an explicit infoWindow: InfoWindow.noText
); let me know if you'd prefer that.
FYI @ditman, I think you were the reviewer on the original PR when it was in the plugins repo, and I see that you're the reviewer here as well. I'm also interested in seeing custom tiles work for the web side of google maps flutter, so I asked @AsturaPhoenix if I could move his PR to the new repo and he's cool with that (thanks AP!). That's the background for this one. Let me know if you have any questions and I can try to answer them or solicit AP's help if needed. Thanks! |
f71eaf0
to
b668a6f
Compare
Resolved a changelog conflict and rebased on main. |
FYI I just moved over my local repo as well, so that's one less hurdle if we do end up needing to collaborate. Thanks for taking over @elitree! |
Update from triage: waiting for @ditman to have review bandwidth for maps PRs. |
3435e6f
to
1ba5ef7
Compare
Rebased on main to resolve conflicts. |
1ba5ef7
to
b7e167e
Compare
@ditman Is this still in your review queue? |
Absolutely |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to tinker with this PR, is there any example
that uses the tile overlays feature so I can see it working? Maybe the google_maps_flutter example app?
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
PS: There seems some tests breaking, it seems that the Mocks need to be regenerated (and committed): https://cirrus-ci.com/task/5045655211606016?logs=drive#L55-L73 (Mocks can be (re)generated with: |
179bff4
to
0b5faf3
Compare
I updated the mocks today as well, thanks for the reminder. |
I did some small changes to the |
I had some code ready to remove |
expect(img.naturalWidth, 0); | ||
expect(img.naturalHeight, 0); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR used to expect that img.hidden
was true
before load, and false
afterwards. Is hidden
really required? In my testing I didn't see any difference so I removed the attribute. @AsturaPhoenix / @elitree do you remember why was the attribute added in the first place, is it required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was to prevent the image decoding from blocking the main thread, but I can't find a version without hidden + decode so I'm not sure I ever ran a comparison. In any case we still have the bottleneck of populating the image data on the Dart/Flutter side, which has to happen on the main thread on web today.
I do think there's a difference in the case of bad image data though; if Tile.data
is not a valid image, I think adding the img
straight and setting the src
will eventually make it display a browser-dependent broken image whereas hiding and decoding wouldn't, but I don't think that was the primary motivation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it can be added back if it becomes a problem. I think a "broken image" (maybe on windows?) is even useful for you to see that there's something fishy going on with your TileProvider!
As for rendering on the main thread, you're right. The only way to avoid that on the web, however, is to have a web-only TileProvider that knows how to delegate the getTile
call to a service worker and an offscreen canvas (in that case the drawing might get more complicated, or you end up with a very big service worker that also includes flutter :P)
/// For consistency with Android, this is not configurable. | ||
// TODO(AsturaPhoenix): Verify consistency with iOS. | ||
static const int logicalTileSize = 256; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am very curious about this logicalTileSize
. From the API it seems that both a TileOverlay and each Tile can specify their size, so this implementation can do away with this logicalTileSize
const.
Why is this fixed to 256, and do we need it?
(If this is 256, how come the example is creating 100x100 tiles? They look blurry on the web as they're scaled up to fit!)
((Also: can tiles be non-square?))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logicalTileSize
is used to communicate to Google Maps the size of the tile for use in coordinate conversions and scaling. This is configurable in the JS API and can be rectangular, but I don't think it's configurable in the Android API. At a logical size of 256 x 256, the web impl appears consistent with the Android impl. I haven't tried it on iOS.
JS API: https://developers.google.com/maps/documentation/javascript/maptypes#MapTypeInterface
Android:
https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/TileOverlay#tile-coordinates
https://developers.google.com/maps/documentation/android-sdk/tileoverlay#coordinates
The physical size can be different and will be scaled on web. I used to use this to display different levels of detail on NOAA charts from a WMS server, effectively requesting higher DPIs to show more features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AsturaPhoenix yes, I used the physical size being logicalSize * dpi
to generate high DPI tiles too. Let's go with consistency for now, having a fixed logical size.
(PS: In iOS they also mention 256x256 tiles or 512x512 for "retina" displays: https://developers.google.com/maps/documentation/ios-sdk/tiles#high_dpi_tiles_for_retina_devices, I'm not sure if the iOS SDK supports sizes any other than those two (like a 3x tile))
packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart
Show resolved
Hide resolved
18003b2
to
a324854
Compare
When testing with the
Not so much on this side of the implementation :) ( |
Yeah, unfortunately the other performance changes probably won't pass muster since they involve changing the API to pass canvases as tiles instead of byte arrays. (There are also unrelated optimizations around serialization of polylines and friends, but unfortunately eventually I ran into an expensive serialization we didn't seem able to avoid within GMS core, so that was the end of that.) This LGTM too; I have one nit comment that I'll add, but it's probably not of consequence. |
Yes, it's quite painful. We can get a few ms back by using bmps instead of pngs, but we don't get jank-free until we pass canvases afaict. (flutter/flutter#116132) |
|
||
controller.update(tileOverlay); | ||
|
||
if (wasVisible || moved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm the tiniest bit concerned that this might also cause any overlays on top of the overlay being updated to reload, but I haven't exercised this case, and my concern might be premature optimization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This little bit of code concerns me a little bit as well, but we'll see in the real world :) I simplified the original if-else a little bit and ended up with this, and I might have over-simplified. I'll tag you whenever the first bug comes about this so you can tell me: "I TOLD YOU SO!" :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh actually you know what, rereading this more carefully I think it does warrant another pass. For example, there actually seems to be a bug (that I almost doubt anyone would hit) where if you change the Z index of a layer that's not visible, it becomes visible. (Purely based on my reading of the code; I haven't run a test, so I might be wrong.)
It'd probably also be worthwhile to add a regression test for this case. (I can also take care of this if nobody gets around to it over the weekend.)
And, since this is in a loop, the impact of remove/insert vs. set for the stable z-index case, although probably still a premature optimization, could be compounded if it were ever actually an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically the fix if we want to do it this way is just to remove the moved
condition entirely, but you know what I'd do if it were up to me.
@AsturaPhoenix in this case I see two optimizations:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I don't think I have permission to push to the remote, but here's the patch (also adding some additional assertions in related tests to try to catch similar regressions):
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart
index 4b95d8bbe..8b6b34694 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart
@@ -68,6 +68,9 @@ void main() {
tileProviders[1].getTile(any, any, any),
tileProviders[2].getTile(any, any, any),
]);
+ verifyNoMoreInteractions(tileProviders[0]);
+ verifyNoMoreInteractions(tileProviders[1]);
+ verifyNoMoreInteractions(tileProviders[2]);
});
testWidgets('changeTileOverlays', (WidgetTester tester) async {
@@ -87,6 +90,8 @@ void main() {
tileProviders[1].getTile(any, any, any),
]);
verifyZeroInteractions(tileProviders[0]);
+ verifyNoMoreInteractions(tileProviders[1]);
+ verifyNoMoreInteractions(tileProviders[2]);
// Re-enable overlay 0.
controller.changeTileOverlays(
@@ -99,6 +104,22 @@ void main() {
tileProviders[0].getTile(any, any, any),
tileProviders[1].getTile(any, any, any),
]);
+ verifyNoMoreInteractions(tileProviders[0]);
+ verifyNoMoreInteractions(tileProviders[1]);
+ verifyNoMoreInteractions(tileProviders[2]);
+ });
+
+ testWidgets(
+ 'updating the z index of a hidden layer does not make it visible',
+ (WidgetTester tester) async {
+ controller.addTileOverlays(<TileOverlay>{...tileOverlays});
+
+ controller.changeTileOverlays(<TileOverlay>{
+ tileOverlays[0].copyWith(zIndexParam: -1, visibleParam: false),
+ });
+
+ probeTiles();
+ verifyZeroInteractions(tileProviders[0]);
});
testWidgets('removeTileOverlays', (WidgetTester tester) async {
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlays.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlays.dart
index 464fb319a..aa6c19173 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlays.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlays.dart
@@ -63,14 +63,13 @@ class TileOverlaysController extends GeometryController {
final bool wasVisible = controller.tileOverlay.visible;
final bool isVisible = tileOverlay.visible;
- final bool moved = tileOverlay.zIndex != controller.tileOverlay.zIndex;
controller.update(tileOverlay);
- if (wasVisible || moved) {
+ if (wasVisible) {
_remove(controller);
}
- if (isVisible || moved) {
+ if (isVisible) {
_insertZSorted(controller);
}
}
|
||
controller.update(tileOverlay); | ||
|
||
if (wasVisible || moved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically the fix if we want to do it this way is just to remove the moved
condition entirely, but you know what I'd do if it were up to me.
a324854
to
bae3ab7
Compare
Hi @AsturaPhoenix, I sent an invite to collaborate, so maybe that will allow you to upload. I did just update the branch to resolve some conflicts, but didn't add your changes yet ( Please feel free to update as you see fit! |
Remove/add based on visibility only.
@AsturaPhoenix, @elitree thanks for the fix over the weekend! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
flutter/packages@60e9a54...3dc00c1 2023-08-01 engine-flutter-autoroll@skia.org Manual roll Flutter from 1d44fbd to 1d59196 (18 revisions) (flutter/packages#4621) 2023-07-31 32242716+ricardoamador@users.noreply.github.com Update the cirrus key jul-31-2023 (flutter/packages#4618) 2023-07-31 jpnurmi@gmail.com [path_provider_platform_interface] Add getApplicationCachePath() (flutter/packages#4614) 2023-07-31 elitree@gmail.com [google_maps_flutter_web] Initial support for custom overlays (flutter/packages#3538) 2023-07-31 34410554+maciejbrzezinski@users.noreply.github.com [camera] Removed the microphone permission request from availableCameras on Web (flutter/packages#4263) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC flutter-ecosystem@google.com,rmistry@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
flutter/packages@60e9a54...3dc00c1 2023-08-01 engine-flutter-autoroll@skia.org Manual roll Flutter from 1d44fbd to 1d59196 (18 revisions) (flutter/packages#4621) 2023-07-31 32242716+ricardoamador@users.noreply.github.com Update the cirrus key jul-31-2023 (flutter/packages#4618) 2023-07-31 jpnurmi@gmail.com [path_provider_platform_interface] Add getApplicationCachePath() (flutter/packages#4614) 2023-07-31 elitree@gmail.com [google_maps_flutter_web] Initial support for custom overlays (flutter/packages#3538) 2023-07-31 34410554+maciejbrzezinski@users.noreply.github.com [camera] Removed the microphone permission request from availableCameras on Web (flutter/packages#4263) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC flutter-ecosystem@google.com,rmistry@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
This is a resubmission of flutter/plugins#6982 from the now archived flutter plugins repo. I'm submitting the changes from the original author, @AsturaPhoenix. The original description is below.
Saves tile bytes to blobs and uses img elements to decode and render. Does not implement opacity, perform caching, or serve placeholder images.
Issue: Fixes flutter/flutter#98596
Known issues:
Pre-launch Checklist
dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style.///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.