flutter_map
flutter_map copied to clipboard
[BUG] Failure to calculate `LatLngBounds.center` for some points
What is the bug?
When an object of class LatLngBounds is created with some values, the subsequent call of the getter "center" on the object causes an AssertionError
How can we reproduce it?
In the real-world scenario, we're using flutter_map_marker_cluster library, which is trying to draw a cluster marker, during this process the "center" getter is called, and the whole layer rendering fails with an AssertionError.
Here is a small test to reproduce the problem:
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:test/test.dart';
void main() {
group('LatLngBounds', () {
test('should calculate center point #1', () async {
final bounds = LatLngBounds(const LatLng(-77.45, -171.16), const LatLng(46.64, 25.88));
final center = bounds.center;
expect(center.latitude, greaterThanOrEqualTo(-90));
expect(center.latitude, lessThanOrEqualTo(90));
expect(center.longitude, greaterThanOrEqualTo(-180));
expect(center.longitude, lessThanOrEqualTo(180));
});
test('should calculate center point #2', () async {
final bounds = LatLngBounds(const LatLng(-0.87, -179.86), const LatLng(84.92, 23.86));
final center = bounds.center;
expect(center.latitude, greaterThanOrEqualTo(-90));
expect(center.latitude, lessThanOrEqualTo(90));
expect(center.longitude, greaterThanOrEqualTo(-180));
expect(center.longitude, lessThanOrEqualTo(180));
});
});
}
and the result is:
dart:core _AssertionError._throwNew
package:latlong2/latlong/LatLng.dart 34:16 new LatLng
package:flutter_map/src/geo/latlng_bounds.dart 121:12 LatLngBounds.center
test/latlng_bounds_test.dart 9:29 main.<fn>.<fn>
'package:latlong2/latlong/LatLng.dart': Failed assertion: line 34 pos 16: 'longitude >= -180 && longitude <= 180': is not true.
dart:core _AssertionError._throwNew
package:latlong2/latlong/LatLng.dart 34:16 new LatLng
package:flutter_map/src/geo/latlng_bounds.dart 121:12 LatLngBounds.center
test/latlng_bounds_test.dart 17:29 main.<fn>.<fn>
'package:latlong2/latlong/LatLng.dart': Failed assertion: line 34 pos 16: 'longitude >= -180 && longitude <= 180': is not true.
Do you have a potential solution?
No response
Platforms
flutter_map-5.0.0
Severity
Erroneous: Prevents normal functioning and causes errors in the console
Running the tests on flutter_map-6.0.0 gives the same result:
dart:core _AssertionError._throwNew
package:latlong2/latlong/LatLng.dart 34:16 new LatLng
package:flutter_map/src/geo/latlng_bounds.dart 111:12 LatLngBounds.center
test/latlng_bounds_test.dart 9:29 main.<fn>.<fn>
'package:latlong2/latlong/LatLng.dart': Failed assertion: line 34 pos 16: 'longitude >= -180 && longitude <= 180': is not true.
dart:core _AssertionError._throwNew
package:latlong2/latlong/LatLng.dart 34:16 new LatLng
package:flutter_map/src/geo/latlng_bounds.dart 111:12 LatLngBounds.center
test/latlng_bounds_test.dart 17:29 main.<fn>.<fn>
'package:latlong2/latlong/LatLng.dart': Failed assertion: line 34 pos 16: 'longitude >= -180 && longitude <= 180': is not true.
So, it may be worth adding some output in there, see what the actual value is, and how it got to that.
So, it may be worth adding some output in there, see what the actual value is, and how it got to that.
Here are the values that are calculated inside the "center" getter:
LatLngBounds.center: -27.2752295399207, -326.54519557683335
LatLngBounds.center: 46.84991848571311, -182.08004769921172
Sorry, there is some serious math inside, I have no idea how it got there :)
The -326 and -182 are wrong, but the others seems sensible possibly.
The -326 and -182 are wrong, but the others seems sensible possibly.
That's right, but the values -326 and -182 are calculated inside the LatLngBounds.center getter from the correct values I give it. That's why my assumption is that there is a defect inside LatLngBounds.center getter.
Able to produce this simply by defining the initialCameraFit on map options, not somewhere else like OnMapReady as in #1684 so these two issues may be related somehow.
initialCameraFit: CameraFit.coordinates(
coordinates: [initialCenter],
),
My initialCenter variable is Brisbane, AU:
Map initial center: LatLng(latitude:-27.470125, longitude:153.021072)
The error was:
The following assertion was thrown building LayoutBuilder: 'package:latlong2/latlong/LatLng.dart': Failed assertion: line 33 pos 16: 'latitude >= -90 && latitude <= 90': is not true.
I've dug into the assertation error and strangely the both values are given NaN double types as I can see when debugging:
Given values: lat=NaN, lng=NaN
I can confirm it works as expected without any error, when I pass the same variable as a standalone parameter to MapOptions like before:
initialCenter: initialCenter,
Hope this helps. Version 6 upgrades are awesome, thank you so much to everyone contributed!
Hi @kirpit, Thanks, this might be a missing puzzle piece. If you've got a moment, can you describe your display setup and window setup - number, size, resolution, position, that kind of thing?
That may be behaving strangely because having one point in a CameraFit doesn't really make sense.
Hi @kirpit, Thanks, this might be a missing puzzle piece. If you've got a moment, can you describe your display setup and window setup - number, size, resolution, position, that kind of thing?
Absolutely, as much as I can..
I'm using an actual physical device Xiaomi Mi 10 (Android API 33) and according to specs here, it has 6.67 inch - 1080x2340 - 24 bit display (sounds right as it's the attached screenshot size): https://www.devicespecifications.com/en/model/3af652f8
https://github.com/fleaflet/flutter_map/assets/1009931/7d16cdc5-a6df-4672-9af8-8e0675940420
I'm not doing anything fancy on the map (yet), couple of markers and "go to my location" stack button on top right that works with flutter_map_location_marker package no issues. There is only an AppBar within a Scaffold and its body is entirely FlutterMap widget without any FutureBuilder etc..
However, I can reproduce the same exact error (with NaN values) in an LG Nexus 5 (1080x1920) in the device simulator:
https://www.devicespecifications.com/en/model/d23f3709
https://github.com/fleaflet/flutter_map/assets/1009931/063c9f7d-e1dc-42d1-baa6-85551d17bdcd
Let me know if I can provide anything else.
Edit: Also, those are my versions and the doctor says everything is fine..
Dart 3.1.0 Flutter 3.12.0 latlong2: ^0.9.0
I've got a new implementation that seems to be almost the same as the current one, but it passes the first test. The second test however, still doesn't pass. The issue is that we're working at the edge of the projection again (-179.86 is just asking for trouble :D).
I've got a new implementation that seems to be almost the same as the current one, but it passes the first test. The second test however, still doesn't pass. The issue is that we're working at the edge of the projection again (-179.86 is just asking for trouble :D).
I was wondering whether there is a way to prove that the result of those calculations is actually correct (not only between the bounds, but in the middle between the two points). Since I faced this problem while using the flutter_map_marker_cluster library, I was wondering how to check that the clusters were placed correctly, and that lead me to that LatLngBounds.center method
Is there currently a workaround to adjust the zoom level based on the bounds or is the only option to downgrade to flutter_map v5?
@LukBukkit I'm not sure what you're referring to?
Hi, thanks for your fast response, and I'm sorry for my inadequate description of the error. I've got multiple coordinates, which I'm showing as a route using a polyling layer on a map. I planned to set the map's initial position using CameraFit.bounds to make the whole route visible. However, due to this issue, I get the same error as @kirpit when applying the CameraFit using the map's property initialCameraFit.
Using the property LatLngBounds.fromPoints(routePositions).center, I can center the map based on the points without an error. But so far, I haven't found a way to set the map's initialZoom based on a list of points that don't involve CameraFit. Is there any workaround for this issue right now, i.e., to set a map's zoom level based on a list of LatLng points? I can't use a uniform zoom level, as my routes have different lengths.
LatLngBounds calculates the midpoint between the bounding box on a sphere.
This doesn't cause issues on smaller bounding boxes but will have issues if the direct way between those two points is near the edges
This example shows the direkt way between -77.45, -171.16 and 46.64, 25.88:
As we use it to calculate the midpoint on the flat projection, we should use a simple flat approach to calculate the midpoint too:
LatLng get center =>
LatLng(((north - south) / 2) + south, ((east - west) / 2) + west);
Still evaluating if this is the only occurence of this bug.
In the code (LatLngBounds.center) I found a link with the following comment about "midpoint":
The longitude can be normalised to −180…+180 using (lon+540)%360-180
Seems to solve the initial issue, right? I've tried, and found "decent" not-crashing values. I have to say that the results are a bit counterintuitive, as @josxha noticed, because of the sphere.
Should we code a clean sphericalCenter with the current code plus the (lon+540)%360-180 fix, and a simpleCenter that would be the half sum of each corner coordinates?