diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a0357b6c..e06a2f0b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ Please consider [donating](https://docs.fleaflet.dev/supporters#support-us) or [ This CHANGELOG does not include every commit and/or PR - it is a hand picked selection of the most important ones. For a full list of changes, please check the GitHub repository releases/tags. +## [8.0.0] - 2024/07/02 + +Migration from `Point` class internally, but this also affects the external API. + +Migration Guide: +- any methods that previously required `Point` now require `Offset`, `Size`, or `Rect` as return values and parameters +- `pointToLatLng` -> `offsetToLatLng` +- `PointExtension` and `OffsetToPointExtension` marked as internal +- `MapController.rotateAroundPoint` now only accepts an Offset + + +Contains the following user-affecting bug fixes: + + ## [7.0.2] - 2024/07/02 > Note that this version causes a technically breaking change by removing `PolygonLayer.useDynamicUpdate` & `PolylineLayer.useDynamicUpdate`, introduced in v7.0.1. However, the implementations for these was broken on introduction, and their intended purpose no longer exists. Therefore, these should not have been used in any capacity, and should not affect any projects. diff --git a/benchmark/crs.dart b/benchmark/crs.dart index af749f26b..292409798 100644 --- a/benchmark/crs.dart +++ b/benchmark/crs.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter_map/src/geo/crs.dart'; import 'package:latlong2/latlong.dart'; @@ -49,14 +49,14 @@ Future main() async { return x + y; })); - results.add(await timedRun('Concrete type: ${crs.code}.latLngToPoint()', () { + results.add(await timedRun('Concrete type: ${crs.code}.latLngToOffset()', () { double x = 0; double y = 0; for (int i = 0; i < N; ++i) { final latlng = LatLng((i % 90).toDouble(), (i % 180).toDouble()); - final p = crs.latLngToPoint(latlng, 1); - x += p.x; - y += p.y; + final p = crs.latLngToOffset(latlng, 1); + x += p.dx; + y += p.dy; } return x + y; })); @@ -84,9 +84,9 @@ Future main() async { double y = 0; for (int i = 0; i < N; ++i) { final latlng = LatLng((i % 90).toDouble(), (i % 180).toDouble()); - final point = crs.latLngToPoint(latlng, 1); - x += point.x; - y += point.y; + final point = crs.latLngToOffset(latlng, 1); + x += point.dx; + y += point.dy; } return x + y; })); @@ -95,7 +95,7 @@ Future main() async { double x = 0; double y = 0; for (int i = 0; i < N; ++i) { - final latlng = crs.pointToLatLng(math.Point(x, y), 1); + final latlng = crs.offsetToLatLng(Offset(x, y), 1); x += latlng.longitude; y += latlng.latitude; } diff --git a/benchmark/point_in_polygon.dart b/benchmark/point_in_polygon.dart index d75a643af..cb32bbea5 100644 --- a/benchmark/point_in_polygon.dart +++ b/benchmark/point_in_polygon.dart @@ -49,7 +49,7 @@ Future main() async { final circle = makeCircle(1000, 1, 0); results.add(await timedRun('In circle', () { - const point = math.Point(0, 0); + const point = Offset.zero; bool yesPlease = true; for (int i = 0; i < N; ++i) { @@ -61,7 +61,7 @@ Future main() async { })); results.add(await timedRun('Not in circle', () { - const point = math.Point(4, 4); + const point = Offset(4, 4); bool noSir = false; for (int i = 0; i < N; ++i) { diff --git a/example/lib/pages/epsg3996_crs.dart b/example/lib/pages/epsg3996_crs.dart index f2eb97e9b..acbcc984f 100644 --- a/example/lib/pages/epsg3996_crs.dart +++ b/example/lib/pages/epsg3996_crs.dart @@ -41,9 +41,9 @@ class EPSG3996PageState extends State { 128, ]; - final epsg3413Bounds = Bounds( - const Point(-4511619, -4511336), - const Point(4510883, 4510996), + final epsg3413Bounds = Rect.fromPoints( + const Offset(-4511619, -4511336), + const Offset(4510883, 4510996), ); maxZoom = resolutions.length - 1; diff --git a/example/lib/pages/latlng_to_screen_point.dart b/example/lib/pages/latlng_to_screen_point.dart index 4b35f2920..8f69d9a1c 100644 --- a/example/lib/pages/latlng_to_screen_point.dart +++ b/example/lib/pages/latlng_to_screen_point.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -23,7 +21,7 @@ class _LatLngToScreenPointPageState extends State { final mapController = MapController(); LatLng? tappedCoords; - Point? tappedPoint; + Offset? tappedPoint; @override void initState() { @@ -52,8 +50,8 @@ class _LatLngToScreenPointPageState extends State { ), onTap: (_, latLng) { final point = mapController.camera - .latLngToScreenPoint(tappedCoords = latLng); - setState(() => tappedPoint = Point(point.x, point.y)); + .latLngToScreenOffset(tappedCoords = latLng); + setState(() => tappedPoint = Offset(point.dx, point.dy)); }, ), children: [ @@ -77,8 +75,8 @@ class _LatLngToScreenPointPageState extends State { ), if (tappedPoint != null) Positioned( - left: tappedPoint!.x - 60 / 2, - top: tappedPoint!.y - 60 / 2, + left: tappedPoint!.dx - 60 / 2, + top: tappedPoint!.dy - 60 / 2, child: const IgnorePointer( child: Icon( Icons.center_focus_strong_outlined, diff --git a/example/lib/pages/screen_point_to_latlng.dart b/example/lib/pages/screen_point_to_latlng.dart index 02d08ccb7..3910191d1 100644 --- a/example/lib/pages/screen_point_to_latlng.dart +++ b/example/lib/pages/screen_point_to_latlng.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/misc/tile_providers.dart'; @@ -95,8 +93,9 @@ class PointToLatlngPage extends State { ); } - void updatePoint(BuildContext context) => setState(() => latLng = - mapController.camera.pointToLatLng(Point(_getPointX(context), pointY))); + void updatePoint(BuildContext context) => + setState(() => latLng = mapController.camera + .screenOffsetToLatLng(Offset(_getPointX(context), pointY))); double _getPointX(BuildContext context) => MediaQuery.sizeOf(context).width / 2; diff --git a/lib/flutter_map.dart b/lib/flutter_map.dart index f7ad8b96d..60be3d81e 100644 --- a/lib/flutter_map.dart +++ b/lib/flutter_map.dart @@ -59,5 +59,3 @@ export 'package:flutter_map/src/map/options/cursor_keyboard_rotation.dart'; export 'package:flutter_map/src/map/options/interaction.dart'; export 'package:flutter_map/src/map/options/options.dart'; export 'package:flutter_map/src/map/widget.dart'; -export 'package:flutter_map/src/misc/bounds.dart'; -export 'package:flutter_map/src/misc/extensions.dart'; diff --git a/lib/src/geo/crs.dart b/lib/src/geo/crs.dart index 41eda28f9..914131730 100644 --- a/lib/src/geo/crs.dart +++ b/lib/src/geo/crs.dart @@ -1,8 +1,8 @@ import 'dart:math' as math hide Point; import 'dart:math' show Point; +import 'dart:ui'; -import 'package:flutter_map/src/misc/bounds.dart'; -import 'package:flutter_map/src/misc/simplify.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; import 'package:proj4dart/proj4dart.dart' as proj4; @@ -51,14 +51,14 @@ abstract class Crs { /// scaled map point. (double, double) latLngToXY(LatLng latlng, double scale); - /// Similar to [latLngToXY] but converts the XY coordinates to a [Point]. - Point latLngToPoint(LatLng latlng, double zoom) { + /// Similar to [latLngToXY] but converts the XY coordinates to an [Offset]. + Offset latLngToOffset(LatLng latlng, double zoom) { final (x, y) = latLngToXY(latlng, scale(zoom)); - return Point(x, y); + return Offset(x, y); } /// Converts a map point to the sphere coordinate (at a certain zoom). - LatLng pointToLatLng(Point point, double zoom); + LatLng offsetToLatLng(Offset point, double zoom); /// Zoom to Scale function. double scale(double zoom) => 256.0 * math.pow(2, zoom); @@ -67,7 +67,7 @@ abstract class Crs { double zoom(double scale) => math.log(scale / 256) / math.ln2; /// Rescales the bounds to a given zoom value. - Bounds? getProjectedBounds(double zoom); + Rect? getProjectedBounds(double zoom); /// Returns true if we want the world to be replicated, longitude-wise. bool get replicatesWorldLongitude => false; @@ -108,26 +108,26 @@ abstract class CrsWithStaticTransformation extends Crs { } @override - LatLng pointToLatLng(Point point, double zoom) { + LatLng offsetToLatLng(Offset point, double zoom) { final (x, y) = _transformation.untransform( - point.x.toDouble(), - point.y.toDouble(), + point.dx, + point.dy, scale(zoom), ); return projection.unprojectXY(x, y); } @override - Bounds? getProjectedBounds(double zoom) { + Rect? getProjectedBounds(double zoom) { if (infinite) return null; final b = projection.bounds!; final s = scale(zoom); - final (minx, miny) = _transformation.transform(b.min.x, b.min.y, s); - final (maxx, maxy) = _transformation.transform(b.max.x, b.max.y, s); - return Bounds( - Point(minx, miny), - Point(maxx, maxy), + final (minx, miny) = _transformation.transform(b.min.dx, b.min.dy, s); + final (maxx, maxy) = _transformation.transform(b.max.dx, b.max.dy, s); + return Rect.fromPoints( + Offset(minx, miny), + Offset(maxx, maxy), ); } } @@ -171,13 +171,13 @@ class Epsg3857 extends CrsWithStaticTransformation { ); @override - Point latLngToPoint(LatLng latlng, double zoom) { + Offset latLngToOffset(LatLng latlng, double zoom) { final (x, y) = _transformation.transform( SphericalMercator.projectLng(latlng.longitude), SphericalMercator.projectLat(latlng.latitude), scale(zoom), ); - return Point(x, y); + return Offset(x, y); } @override @@ -222,7 +222,7 @@ class Proj4Crs extends Crs { required String code, required proj4.Projection proj4Projection, List>? origins, - Bounds? bounds, + Rect? bounds, List? scales, List? resolutions, }) { @@ -282,10 +282,10 @@ class Proj4Crs extends Crs { /// Converts a map point to the sphere coordinate (at a certain zoom). @override - LatLng pointToLatLng(Point point, double zoom) { + LatLng offsetToLatLng(Offset point, double zoom) { final (x, y) = _getTransformationByZoom(zoom).untransform( - point.x.toDouble(), - point.y.toDouble(), + point.dx, + point.dy, scale(zoom), ); return projection.unprojectXY(x, y); @@ -293,19 +293,18 @@ class Proj4Crs extends Crs { /// Rescales the bounds to a given zoom value. @override - Bounds? getProjectedBounds(double zoom) { + Rect? getProjectedBounds(double zoom) { if (infinite) return null; final b = projection.bounds!; final zoomScale = scale(zoom); final transformation = _getTransformationByZoom(zoom); - final (minx, miny) = transformation.transform(b.min.x, b.min.y, zoomScale); - final (maxx, maxy) = transformation.transform(b.max.x, b.max.y, zoomScale); - return Bounds( - Point(minx, miny), - Point(maxx, maxy), - ); + final (minx, miny) = + transformation.transform(b.min.dx, b.min.dy, zoomScale); + final (maxx, maxy) = + transformation.transform(b.max.dx, b.max.dy, zoomScale); + return Rect.fromPoints(Offset(minx, miny), Offset(maxx, maxy)); } /// Zoom to Scale function. @@ -371,18 +370,18 @@ class Proj4Crs extends Crs { /// Inherit from this class if you want to create or implement your own CRS. @immutable abstract class Projection { - /// The [Bounds] for the coordinates of this [Projection]. - final Bounds? bounds; + /// The bounds for the coordinates of this [Projection]. + final Rect? bounds; /// Base constructor for the abstract [Projection] class that sets the /// required fields. const Projection(this.bounds); - /// Converts a [LatLng] to a coordinates and returns them as [Point] object. + /// Converts a [LatLng] to a coordinates and returns them as an [Offset]. @nonVirtual - Point project(LatLng latlng) { + Offset project(LatLng latlng) { final (x, y) = projectXY(latlng); - return Point(x, y); + return Offset(x, y); } /// Converts a [LatLng] to geometry coordinates. @@ -418,10 +417,10 @@ abstract class Projection { /// longitudes -179 and 179 to be projected each on one side. /// [referencePoint] is used for polygon holes: we want the holes to be /// displayed close to the polygon, not on the other side of the world. - List projectList(List points, {LatLng? referencePoint}) { + List projectList(List points, {LatLng? referencePoint}) { late double previousX; final worldWidth = getWorldWidth(); - return List.generate( + return List.generate( points.length, (j) { if (j == 0 && referencePoint != null) { @@ -436,7 +435,7 @@ abstract class Projection { } } previousX = x; - return DoublePoint(x, y); + return Offset(x, y); }, growable: false, ); @@ -444,10 +443,7 @@ abstract class Projection { } class _LonLat extends Projection { - static const _bounds = Bounds.unsafe( - Point(-180, -90), - Point(180, 90), - ); + static const _bounds = Rect.fromLTRB(-180, -90, 180, 90); const _LonLat() : super(_bounds); @@ -472,9 +468,11 @@ class SphericalMercator extends Projection { static const double _boundsD = r * math.pi; /// The constant Bounds of the [SphericalMercator] projection. - static const Bounds _bounds = Bounds.unsafe( - Point(-_boundsD, -_boundsD), - Point(_boundsD, _boundsD), + static const Rect _bounds = Rect.fromLTRB( + -_boundsD, + -_boundsD, + _boundsD, + _boundsD, ); /// Constant constructor for the [SphericalMercator] projection. @@ -518,7 +516,7 @@ class _Proj4Projection extends Projection { _Proj4Projection({ required this.proj4Projection, - required Bounds? bounds, + required Rect? bounds, }) : epsg4326 = proj4.Projection.WGS84, super(bounds); diff --git a/lib/src/gestures/map_interactive_viewer.dart b/lib/src/gestures/map_interactive_viewer.dart index ebe756de5..46da7b911 100644 --- a/lib/src/gestures/map_interactive_viewer.dart +++ b/lib/src/gestures/map_interactive_viewer.dart @@ -5,6 +5,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; import 'package:vector_math/vector_math_64.dart'; @@ -381,7 +382,9 @@ class MapInteractiveViewerState extends State ); if (_interactionOptions.cursorKeyboardRotationOptions.behaviour == - CursorRotationBehaviour.setNorth) _ckrClickDegrees = 0; + CursorRotationBehaviour.setNorth) { + _ckrClickDegrees = 0; + } } void _onPointerSignal(PointerSignalEvent pointerSignal) { @@ -403,7 +406,7 @@ class MapInteractiveViewerState extends State .clamp(minZoom, maxZoom); // Calculate offset of mouse cursor from viewport center final newCenter = _camera.focusedZoomCenter( - pointerSignal.localPosition.toPoint(), + pointerSignal.localPosition, newZoom, ); widget.controller.moveRaw( @@ -628,16 +631,19 @@ class MapInteractiveViewerState extends State ScaleUpdateDetails details, double zoomAfterPinchZoom, ) { - final oldCenterPt = _camera.project(_camera.center, zoomAfterPinchZoom); + final oldCenterPt = + _camera.projectAtZoom(_camera.center, zoomAfterPinchZoom); final newFocalLatLong = _camera.offsetToCrs(_focalStartLocal, zoomAfterPinchZoom); - final newFocalPt = _camera.project(newFocalLatLong, zoomAfterPinchZoom); - final oldFocalPt = _camera.project(_focalStartLatLng, zoomAfterPinchZoom); + final newFocalPt = + _camera.projectAtZoom(newFocalLatLong, zoomAfterPinchZoom); + final oldFocalPt = + _camera.projectAtZoom(_focalStartLatLng, zoomAfterPinchZoom); final zoomDifference = oldFocalPt - newFocalPt; final moveDifference = _rotateOffset(_focalStartLocal - _lastFocalLocal); - final newCenterPt = oldCenterPt + zoomDifference + moveDifference.toPoint(); - return _camera.unproject(newCenterPt, zoomAfterPinchZoom); + final newCenterPt = oldCenterPt + zoomDifference + moveDifference; + return _camera.unprojectAtZoom(newCenterPt, zoomAfterPinchZoom); } void _handleScalePinchRotate( @@ -651,15 +657,15 @@ class MapInteractiveViewerState extends State if (_rotationStarted) { final rotationDiff = currentRotation - _lastRotation; - final oldCenterPt = _camera.project(_camera.center); + final oldCenterPt = _camera.projectAtZoom(_camera.center); final rotationCenter = - _camera.project(_camera.offsetToCrs(_lastFocalLocal)); + _camera.projectAtZoom(_camera.offsetToCrs(_lastFocalLocal)); final vector = oldCenterPt - rotationCenter; final rotatedVector = vector.rotate(degrees2Radians * rotationDiff); final newCenter = rotationCenter + rotatedVector; widget.controller.moveAndRotateRaw( - _camera.unproject(newCenter), + _camera.unprojectAtZoom(newCenter), _camera.zoom, _camera.rotation + rotationDiff, offset: Offset.zero, @@ -728,9 +734,7 @@ class MapInteractiveViewerState extends State } final direction = details.velocity.pixelsPerSecond / magnitude; - final distance = - (Offset.zero & Size(_camera.nonRotatedSize.x, _camera.nonRotatedSize.y)) - .shortestSide; + final distance = (Offset.zero & _camera.nonRotatedSize).shortestSide; final flingOffset = _focalStartLocal - _lastFocalLocal; _flingAnimation = Tween( @@ -803,7 +807,7 @@ class MapInteractiveViewerState extends State if (InteractiveFlag.hasDoubleTapZoom(_interactionOptions.flags)) { final newZoom = _getZoomForScale(_camera.zoom, 2); final newCenter = _camera.focusedZoomCenter( - tapPosition.relative!.toPoint(), + tapPosition.relative!, newZoom, ); _startDoubleTapAnimation(newZoom, newCenter); @@ -881,24 +885,24 @@ class MapInteractiveViewerState extends State _startListeningForAnimationInterruptions(); } - final newCenterPoint = _camera.project(_mapCenterStart) + - _flingAnimation.value.toPoint().rotate(_camera.rotationRad); + final newCenterPoint = _camera.projectAtZoom(_mapCenterStart) + + _flingAnimation.value.rotate(_camera.rotationRad); final LatLng newCenter; if (!_camera.crs.replicatesWorldLongitude) { - newCenter = _camera.unproject(newCenterPoint); + newCenter = _camera.unprojectAtZoom(newCenterPoint); } else { - final math.Point bestCenterPoint; + final Offset bestCenterPoint; final double worldSize = _camera.crs.scale(_camera.zoom); - if (newCenterPoint.x > worldSize) { + if (newCenterPoint.dx > worldSize) { bestCenterPoint = - math.Point(newCenterPoint.x - worldSize, newCenterPoint.y); - } else if (newCenterPoint.x < 0) { + Offset(newCenterPoint.dx - worldSize, newCenterPoint.dy); + } else if (newCenterPoint.dx < 0) { bestCenterPoint = - math.Point(newCenterPoint.x + worldSize, newCenterPoint.y); + Offset(newCenterPoint.dx + worldSize, newCenterPoint.dy); } else { bestCenterPoint = newCenterPoint; } - newCenter = _camera.unproject(bestCenterPoint); + newCenter = _camera.unprojectAtZoom(bestCenterPoint); } widget.controller.moveRaw( diff --git a/lib/src/layer/circle_layer/circle_layer.dart b/lib/src/layer/circle_layer/circle_layer.dart index 98e2235ec..8090fa582 100644 --- a/lib/src/layer/circle_layer/circle_layer.dart +++ b/lib/src/layer/circle_layer/circle_layer.dart @@ -36,7 +36,7 @@ class CircleLayer extends StatelessWidget { camera: camera, hitNotifier: hitNotifier, ), - size: Size(camera.size.x, camera.size.y), + size: camera.size, isComplex: true, ), ); diff --git a/lib/src/layer/circle_layer/painter.dart b/lib/src/layer/circle_layer/painter.dart index e0e0ab684..a87bc8fd5 100644 --- a/lib/src/layer/circle_layer/painter.dart +++ b/lib/src/layer/circle_layer/painter.dart @@ -20,7 +20,7 @@ base class CirclePainter @override bool elementHitTest( CircleMarker element, { - required Point point, + required Offset point, required LatLng coordinate, }) { final circle = element; // Should be optimized out by compiler, avoids lint @@ -33,7 +33,7 @@ base class CirclePainter .distance : circle.radius; - return pow(point.x - center.dx, 2) + pow(point.y - center.dy, 2) <= + return pow(point.dx - center.dx, 2) + pow(point.dy - center.dy, 2) <= radius * radius; } diff --git a/lib/src/layer/marker_layer/marker_layer.dart b/lib/src/layer/marker_layer/marker_layer.dart index f976d8dca..2d6c7c369 100644 --- a/lib/src/layer/marker_layer/marker_layer.dart +++ b/lib/src/layer/marker_layer/marker_layer.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; @@ -49,21 +47,24 @@ class MarkerLayer extends StatelessWidget { children: (List markers) sync* { for (final m in markers) { // Resolve real alignment + // TODO this can probably just be done with calls to Size, Offset, and Rect final left = 0.5 * m.width * ((m.alignment ?? alignment).x + 1); final top = 0.5 * m.height * ((m.alignment ?? alignment).y + 1); final right = m.width - left; final bottom = m.height - top; // Perform projection - final pxPoint = map.project(m.point); + final pxPoint = map.projectAtZoom(m.point); // Cull if out of bounds - if (!map.pixelBounds.containsPartialBounds( - Bounds( - Point(pxPoint.x + left, pxPoint.y - bottom), - Point(pxPoint.x - right, pxPoint.y + top), + if (!map.pixelBounds.overlaps( + Rect.fromPoints( + Offset(pxPoint.dx + left, pxPoint.dy - bottom), + Offset(pxPoint.dx - right, pxPoint.dy + top), ), - )) continue; + )) { + continue; + } // Apply map camera to marker position final pos = pxPoint - map.pixelOrigin; @@ -72,8 +73,8 @@ class MarkerLayer extends StatelessWidget { key: m.key, width: m.width, height: m.height, - left: pos.x - right, - top: pos.y - bottom, + left: pos.dx - right, + top: pos.dy - bottom, child: (m.rotate ?? rotate) ? Transform.rotate( angle: -map.rotationRad, diff --git a/lib/src/layer/overlay_image_layer/overlay_image.dart b/lib/src/layer/overlay_image_layer/overlay_image.dart index 22ab1565b..703e7550b 100644 --- a/lib/src/layer/overlay_image_layer/overlay_image.dart +++ b/lib/src/layer/overlay_image_layer/overlay_image.dart @@ -82,16 +82,16 @@ class OverlayImage extends BaseOverlayImage { final camera = MapCamera.of(context); // northWest is not necessarily upperLeft depending on projection - final bounds = Bounds( - camera.project(this.bounds.northWest) - camera.pixelOrigin, - camera.project(this.bounds.southEast) - camera.pixelOrigin, + final bounds = Rect.fromPoints( + camera.projectAtZoom(this.bounds.northWest) - camera.pixelOrigin, + camera.projectAtZoom(this.bounds.southEast) - camera.pixelOrigin, ); return Positioned( - left: bounds.topLeft.x, - top: bounds.topLeft.y, - width: bounds.size.x, - height: bounds.size.y, + left: bounds.topLeft.dx, + top: bounds.topLeft.dy, + width: bounds.size.width, + height: bounds.size.height, child: child, ); } @@ -135,36 +135,36 @@ class RotatedOverlayImage extends BaseOverlayImage { }) { final camera = MapCamera.of(context); - final pxTopLeft = camera.project(topLeftCorner) - camera.pixelOrigin; + final pxTopLeft = camera.projectAtZoom(topLeftCorner) - camera.pixelOrigin; final pxBottomRight = - camera.project(bottomRightCorner) - camera.pixelOrigin; - final pxBottomLeft = camera.project(bottomLeftCorner) - camera.pixelOrigin; + camera.projectAtZoom(bottomRightCorner) - camera.pixelOrigin; + final pxBottomLeft = + camera.projectAtZoom(bottomLeftCorner) - camera.pixelOrigin; /// calculate pixel coordinate of top-right corner by calculating the /// vector from bottom-left to top-left and adding it to bottom-right final pxTopRight = pxTopLeft - pxBottomLeft + pxBottomRight; /// update/enlarge bounds so the new corner points fit within - final bounds = Bounds(pxTopLeft, pxBottomRight) - .extend(pxTopRight) - .extend(pxBottomLeft); + final bounds = RectExtension.containing( + [pxTopLeft, pxBottomRight, pxTopRight, pxBottomLeft]); - final vectorX = (pxTopRight - pxTopLeft) / bounds.size.x; - final vectorY = (pxBottomLeft - pxTopLeft) / bounds.size.y; + final vectorX = (pxTopRight - pxTopLeft) / bounds.size.width; + final vectorY = (pxBottomLeft - pxTopLeft) / bounds.size.height; final offset = pxTopLeft - bounds.topLeft; - final a = vectorX.x; - final b = vectorX.y; - final c = vectorY.x; - final d = vectorY.y; - final tx = offset.x; - final ty = offset.y; + final a = vectorX.dx; + final b = vectorX.dy; + final c = vectorY.dx; + final d = vectorY.dy; + final tx = offset.dx; + final ty = offset.dy; return Positioned( - left: bounds.topLeft.x, - top: bounds.topLeft.y, - width: bounds.size.x, - height: bounds.size.y, + left: bounds.topLeft.dx, + top: bounds.topLeft.dy, + width: bounds.size.width, + height: bounds.size.height, child: Transform( transform: Matrix4(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1), filterQuality: filterQuality, diff --git a/lib/src/layer/overlay_image_layer/overlay_image_layer.dart b/lib/src/layer/overlay_image_layer/overlay_image_layer.dart index 7e7922781..38d2ea75d 100644 --- a/lib/src/layer/overlay_image_layer/overlay_image_layer.dart +++ b/lib/src/layer/overlay_image_layer/overlay_image_layer.dart @@ -1,5 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; part 'overlay_image.dart'; diff --git a/lib/src/layer/polygon_layer/label.dart b/lib/src/layer/polygon_layer/label.dart index 372cdead0..39878c2ab 100644 --- a/lib/src/layer/polygon_layer/label.dart +++ b/lib/src/layer/polygon_layer/label.dart @@ -1,7 +1,7 @@ part of 'polygon_layer.dart'; void Function(Canvas canvas)? _buildLabelTextPainter({ - required math.Point mapSize, + required Size mapSize, required Offset placementPoint, required ({Offset min, Offset max}) bounds, required TextPainter textPainter, @@ -18,10 +18,10 @@ void Function(Canvas canvas)? _buildLabelTextPainter({ // Currently this is only enabled when the map isn't rotated, since the placementOffset // is relative to the MobileLayerTransformer rather than in actual screen coordinates. if (rotationRad == 0) { - if (dx + width / 2 < 0 || dx - width / 2 > mapSize.x) { + if (dx + width / 2 < 0 || dx - width / 2 > mapSize.width) { return null; } - if (dy + height / 2 < 0 || dy - height / 2 > mapSize.y) { + if (dy + height / 2 < 0 || dy - height / 2 > mapSize.height) { return null; } } @@ -77,8 +77,8 @@ LatLng _computeCentroid(List points) { LatLng _computePolylabel(List points) { final labelPosition = polylabel( [ - List.generate(points.length, - (i) => math.Point(points[i].longitude, points[i].latitude)), + List>.generate(points.length, + (i) => Point(points[i].longitude, points[i].latitude)), ], // "precision" is a bit of a misnomer. It's a threshold for when to stop // dividing-and-conquering the polygon in the hopes of finding a better diff --git a/lib/src/layer/polygon_layer/painter.dart b/lib/src/layer/polygon_layer/painter.dart index 91ed21a74..22cfc2f35 100644 --- a/lib/src/layer/polygon_layer/painter.dart +++ b/lib/src/layer/polygon_layer/painter.dart @@ -49,7 +49,7 @@ base class _PolygonPainter @override bool elementHitTest( _ProjectedPolygon projectedPolygon, { - required math.Point point, + required Offset point, required LatLng coordinate, }) { // TODO: We should check the bounding box here, for efficiency @@ -193,7 +193,8 @@ base class _PolygonPainter lastHash = null; } - final origin = (camera.project(camera.center) - camera.size / 2).toOffset(); + final origin = + camera.projectAtZoom(camera.center) - camera.size.center(Offset.zero); // Main loop constructing batched fill and border paths from given polygons. for (int i = 0; i <= polygons.length - 1; i++) { diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 0f310ea3d..34b289029 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -1,4 +1,4 @@ -import 'dart:math' as math; +import 'dart:math'; import 'dart:ui'; import 'package:collection/collection.dart'; @@ -152,8 +152,8 @@ class _PolygonLayerState extends State> List.generate( points.length * 2, (ii) => ii.isEven - ? points.elementAt(ii ~/ 2).x - : points.elementAt(ii ~/ 2).y, + ? points.elementAt(ii ~/ 2).dx + : points.elementAt(ii ~/ 2).dy, growable: false, ), holeIndices: culledPolygon.holePoints.isEmpty @@ -176,7 +176,7 @@ class _PolygonLayerState extends State> debugAltRenderer: widget.debugAltRenderer, hitNotifier: widget.hitNotifier, ), - size: Size(camera.size.x, camera.size.y), + size: camera.size, ), ); } diff --git a/lib/src/layer/polygon_layer/projected_polygon.dart b/lib/src/layer/polygon_layer/projected_polygon.dart index af18c74be..5796c42bc 100644 --- a/lib/src/layer/polygon_layer/projected_polygon.dart +++ b/lib/src/layer/polygon_layer/projected_polygon.dart @@ -3,8 +3,8 @@ part of 'polygon_layer.dart'; @immutable class _ProjectedPolygon with HitDetectableElement { final Polygon polygon; - final List points; - final List> holePoints; + final List points; + final List> holePoints; @override R? get hitValue => polygon.hitValue; @@ -25,10 +25,10 @@ class _ProjectedPolygon with HitDetectableElement { holes.isEmpty || polygon.points.isEmpty || holes.every((e) => e.isEmpty)) { - return >[]; + return >[]; } - return List>.generate( + return List>.generate( holes.length, (j) => projection.projectList( holes[j], diff --git a/lib/src/layer/polyline_layer/painter.dart b/lib/src/layer/polyline_layer/painter.dart index 1d85b57df..44bf498b0 100644 --- a/lib/src/layer/polyline_layer/painter.dart +++ b/lib/src/layer/polyline_layer/painter.dart @@ -20,7 +20,7 @@ base class _PolylinePainter @override bool elementHitTest( _ProjectedPolyline projectedPolyline, { - required math.Point point, + required Offset point, required LatLng coordinate, }) { final polyline = projectedPolyline.polyline; @@ -57,7 +57,7 @@ base class _PolylinePainter final o2 = offsets[i + 1]; final distanceSq = - getSqSegDist(point.x, point.y, o1.dx, o1.dy, o2.dx, o2.dy); + getSqSegDist(point.dx, point.dy, o1.dx, o1.dy, o2.dx, o2.dy); if (distanceSq <= hittableDistance * hittableDistance) return true; } @@ -108,7 +108,7 @@ base class _PolylinePainter } final origin = - camera.project(camera.center).toOffset() - camera.size.toOffset() / 2; + camera.projectAtZoom(camera.center) - camera.size.center(Offset.zero); for (final projectedPolyline in polylines) { final polyline = projectedPolyline.polyline; @@ -284,8 +284,8 @@ base class _PolylinePainter return delta.distance; } - LatLng _unproject(DoublePoint p0) => - camera.crs.projection.unprojectXY(p0.x, p0.y); + LatLng _unproject(Offset p0) => + camera.crs.projection.unprojectXY(p0.dx, p0.dy); @override bool shouldRepaint(_PolylinePainter oldDelegate) => diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index 541e73c8b..f905afca1 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -9,6 +9,7 @@ import 'package:flutter_map/src/layer/shared/layer_interactivity/internal_hit_de import 'package:flutter_map/src/layer/shared/layer_projection_simplification/state.dart'; import 'package:flutter_map/src/layer/shared/layer_projection_simplification/widget.dart'; import 'package:flutter_map/src/layer/shared/line_patterns/pixel_hiker.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:flutter_map/src/misc/offsets.dart'; import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart'; @@ -111,7 +112,7 @@ class _PolylineLayerState extends State> hitNotifier: widget.hitNotifier, minimumHitbox: widget.minimumHitbox, ), - size: Size(camera.size.x, camera.size.y), + size: camera.size, ), ); } @@ -135,7 +136,7 @@ class _PolylineLayerState extends State> ); // segment is visible - final projBounds = Bounds( + final projBounds = Rect.fromPoints( projection.project(boundsAdjusted.southWest), projection.project(boundsAdjusted.northEast), ); @@ -158,7 +159,7 @@ class _PolylineLayerState extends State> /// Returns true if the points stretch on different versions of the world. bool stretchesBeyondTheLimits() { for (final point in projectedPolyline.points) { - if (point.x > xEast || point.x < xWest) { + if (point.dx > xEast || point.dx < xWest) { return true; } } @@ -179,7 +180,8 @@ class _PolylineLayerState extends State> final p1 = projectedPolyline.points[i]; final p2 = projectedPolyline.points[i + 1]; - containsSegment = projBounds.aabbContainsLine(p1.x, p1.y, p2.x, p2.y); + containsSegment = + projBounds.aabbContainsLine(p1.dx, p1.dy, p2.dx, p2.dy); if (containsSegment) { if (start == -1) { start = i; diff --git a/lib/src/layer/polyline_layer/projected_polyline.dart b/lib/src/layer/polyline_layer/projected_polyline.dart index 9ba15e4f5..eb41ae2fc 100644 --- a/lib/src/layer/polyline_layer/projected_polyline.dart +++ b/lib/src/layer/polyline_layer/projected_polyline.dart @@ -3,7 +3,7 @@ part of 'polyline_layer.dart'; @immutable class _ProjectedPolyline with HitDetectableElement { final Polyline polyline; - final List points; + final List points; @override R? get hitValue => polyline.hitValue; diff --git a/lib/src/layer/scalebar/scalebar.dart b/lib/src/layer/scalebar/scalebar.dart index b65fba9cc..477b6e146 100644 --- a/lib/src/layer/scalebar/scalebar.dart +++ b/lib/src/layer/scalebar/scalebar.dart @@ -69,7 +69,7 @@ class Scalebar extends StatelessWidget { // calculate the scalebar width in pixels final latLngCenter = camera.center; - final offsetCenter = camera.project(latLngCenter); + final offsetCenter = camera.projectAtZoom(latLngCenter); final absLat = latLngCenter.latitude.abs(); double index = camera.zoom - length.value; @@ -88,14 +88,14 @@ class Scalebar extends StatelessWidget { if (latLngOffset.longitude < latLngCenter.longitude) { latLngOffset = dst.offset(latLngCenter, metricDst.toDouble(), 270); } - final offsetDistance = camera.project(latLngOffset); + final offsetDistance = camera.projectAtZoom(latLngOffset); final label = metricDst < 1000 ? '$metricDst m' : '${(metricDst / 1000.0).toStringAsFixed(0)} km'; final ScalebarPainter scalebarPainter = _SimpleScalebarPainter( // use .abs() to avoid wrong placements on the right map border - scalebarLength: (offsetDistance.x - offsetCenter.x).abs(), + scalebarLength: (offsetDistance.dx - offsetCenter.dx).abs(), text: TextSpan( style: textStyle, text: label, diff --git a/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart b/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart index 3a9c3c8ee..b59135d7a 100644 --- a/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart +++ b/lib/src/layer/shared/layer_interactivity/internal_hit_detectable.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; @@ -41,8 +39,8 @@ abstract base class HitDetectablePainter point, + required Offset point, required LatLng coordinate, }); @@ -66,8 +64,8 @@ abstract base class HitDetectablePainter= 0; i--) { final element = elements.elementAt(i); @@ -98,7 +96,7 @@ base mixin HitTestRequiresCameraOrigin { /// Screen point of the detected hit /// /// See [coordinate] for the geographical coordinate which was hit. - final Point point; + final Offset point; /// Construct a new [LayerHitResult] @internal diff --git a/lib/src/layer/shared/mobile_layer_transformer.dart b/lib/src/layer/shared/mobile_layer_transformer.dart index 7346f35a5..092bafad2 100644 --- a/lib/src/layer/shared/mobile_layer_transformer.dart +++ b/lib/src/layer/shared/mobile_layer_transformer.dart @@ -17,10 +17,10 @@ class MobileLayerTransformer extends StatelessWidget { final camera = MapCamera.of(context); return OverflowBox( - minWidth: camera.size.x, - maxWidth: camera.size.x, - minHeight: camera.size.y, - maxHeight: camera.size.y, + minWidth: camera.size.width, + maxWidth: camera.size.width, + minHeight: camera.size.height, + maxHeight: camera.size.height, child: Transform.rotate(angle: camera.rotationRad, child: child), ); } diff --git a/lib/src/layer/tile_layer/tile.dart b/lib/src/layer/tile_layer/tile.dart index 9eff386c4..a713c51d4 100644 --- a/lib/src/layer/tile_layer/tile.dart +++ b/lib/src/layer/tile_layer/tile.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -19,7 +17,7 @@ class Tile extends StatefulWidget { /// Reference to the offset of the top-left corner of the bounding rectangle /// of the [MapCamera]. The origin will not equal the offset of the top-left /// visible pixel when the map is rotated. - final Point currentPixelOrigin; + final Offset currentPixelOrigin; /// Position Coordinates. /// @@ -71,9 +69,9 @@ class _TileState extends State { Widget build(BuildContext context) { return Positioned( left: widget.positionCoordinates.x * widget.scaledTileDimension - - widget.currentPixelOrigin.x, + widget.currentPixelOrigin.dx, top: widget.positionCoordinates.y * widget.scaledTileDimension - - widget.currentPixelOrigin.y, + widget.currentPixelOrigin.dy, width: widget.scaledTileDimension, height: widget.scaledTileDimension, child: widget.tileBuilder?.call(context, _tileImage, widget.tileImage) ?? diff --git a/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart b/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart index f2ef8b1d1..4f116536e 100644 --- a/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart +++ b/lib/src/layer/tile_layer/tile_bounds/tile_bounds.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds_at_zoom.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; @@ -86,13 +88,13 @@ class DiscreteTileBounds extends TileBounds { TileBoundsAtZoom _tileBoundsAtZoomImpl(int zoom) { final zoomDouble = zoom.toDouble(); - final Bounds pixelBounds; + final Rect pixelBounds; if (_latLngBounds == null) { pixelBounds = crs.getProjectedBounds(zoomDouble)!; } else { - pixelBounds = Bounds( - crs.latLngToPoint(_latLngBounds!.southWest, zoomDouble), - crs.latLngToPoint(_latLngBounds!.northEast, zoomDouble), + pixelBounds = Rect.fromPoints( + crs.latLngToOffset(_latLngBounds!.southWest, zoomDouble), + crs.latLngToOffset(_latLngBounds!.northEast, zoomDouble), ); } @@ -126,35 +128,39 @@ class WrappedTileBounds extends TileBounds { WrappedTileBoundsAtZoom _tileBoundsAtZoomImpl(int zoom) { final zoomDouble = zoom.toDouble(); - final Bounds pixelBounds; + final Rect pixelBounds; if (_latLngBounds == null) { pixelBounds = crs.getProjectedBounds(zoomDouble)!; } else { - pixelBounds = Bounds( - crs.latLngToPoint(_latLngBounds!.southWest, zoomDouble), - crs.latLngToPoint(_latLngBounds!.northEast, zoomDouble), + pixelBounds = Rect.fromPoints( + crs.latLngToOffset(_latLngBounds!.southWest, zoomDouble), + crs.latLngToOffset(_latLngBounds!.northEast, zoomDouble), ); } (int, int)? wrapX; if (crs.wrapLng case final wrapLng?) { - final wrapXMin = (crs.latLngToPoint(LatLng(0, wrapLng.$1), zoomDouble).x / - _tileDimension) - .floor(); - final wrapXMax = (crs.latLngToPoint(LatLng(0, wrapLng.$2), zoomDouble).x / - _tileDimension) - .ceil(); + final wrapXMin = + (crs.latLngToOffset(LatLng(0, wrapLng.$1), zoomDouble).dx / + _tileDimension) + .floor(); + final wrapXMax = + (crs.latLngToOffset(LatLng(0, wrapLng.$2), zoomDouble).dx / + _tileDimension) + .ceil(); wrapX = (wrapXMin, wrapXMax - 1); } (int, int)? wrapY; if (crs.wrapLat case final wrapLat?) { - final wrapYMin = (crs.latLngToPoint(LatLng(wrapLat.$1, 0), zoomDouble).y / - _tileDimension) - .floor(); - final wrapYMax = (crs.latLngToPoint(LatLng(wrapLat.$2, 0), zoomDouble).y / - _tileDimension) - .ceil(); + final wrapYMin = + (crs.latLngToOffset(LatLng(wrapLat.$1, 0), zoomDouble).dy / + _tileDimension) + .floor(); + final wrapYMax = + (crs.latLngToOffset(LatLng(wrapLat.$2, 0), zoomDouble).dy / + _tileDimension) + .ceil(); wrapY = (wrapYMin, wrapYMax - 1); } diff --git a/lib/src/layer/tile_layer/tile_layer.dart b/lib/src/layer/tile_layer/tile_layer.dart index 3ff6cdbda..d9a278b8b 100644 --- a/lib/src/layer/tile_layer/tile_layer.dart +++ b/lib/src/layer/tile_layer/tile_layer.dart @@ -12,6 +12,7 @@ import 'package:flutter_map/src/layer/tile_layer/tile_image_manager.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range_calculator.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_scale_calculator.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:http/http.dart'; import 'package:http/retry.dart'; import 'package:logger/logger.dart'; @@ -695,8 +696,9 @@ class _TileLayerState extends State with TickerProviderStateMixin { // Re-order the tiles by their distance to the center of the range. final tileCenter = expandedTileLoadRange.center; tilesToLoad.sort( - (a, b) => _distanceSq(a.coordinates, tileCenter) - .compareTo(_distanceSq(b.coordinates, tileCenter)), + (a, b) => (a.coordinates.toOffset() - tileCenter) + .distanceSquared + .compareTo((b.coordinates.toOffset() - tileCenter).distanceSquared), ); // Create the new Tiles. @@ -757,9 +759,3 @@ class _TileLayerState extends State with TickerProviderStateMixin { if (mounted) _loadAndPruneInVisibleBounds(MapCamera.of(context)); } } - -double _distanceSq(TileCoordinates coord, Point center) { - final dx = center.x - coord.x; - final dy = center.y - coord.y; - return dx * dx + dy * dy; -} diff --git a/lib/src/layer/tile_layer/tile_range.dart b/lib/src/layer/tile_layer/tile_range.dart index 41eff6ee0..b7f2b90d8 100644 --- a/lib/src/layer/tile_layer/tile_range.dart +++ b/lib/src/layer/tile_layer/tile_range.dart @@ -1,7 +1,10 @@ import 'dart:math' as math hide Point; import 'dart:math' show Point; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:meta/meta.dart'; /// A range of tiles, this is normally a [DiscreteTileRange] and sometimes @@ -29,11 +32,16 @@ class EmptyTileRange extends TileRange { const Iterable.empty(); } +Point _floor(Offset point) => + Point(point.dx.floor(), point.dy.floor()); + +Point _ceil(Offset point) => Point(point.dx.ceil(), point.dy.ceil()); + /// Every [TileRange] is a [DiscreteTileRange] if it's not an [EmptyTileRange]. @immutable class DiscreteTileRange extends TileRange { /// Bounds are inclusive - final Bounds _bounds; + final IntegerBounds _bounds; /// Create a new [DiscreteTileRange] by setting it's values. const DiscreteTileRange(super.zoom, this._bounds); @@ -42,16 +50,16 @@ class DiscreteTileRange extends TileRange { factory DiscreteTileRange.fromPixelBounds({ required int zoom, required int tileDimension, - required Bounds pixelBounds, + required Rect pixelBounds, }) { - final Bounds bounds; - if (pixelBounds.min == pixelBounds.max) { - final minAndMax = (pixelBounds.min / tileDimension).floor(); - bounds = Bounds(minAndMax, minAndMax); + final IntegerBounds bounds; + if (pixelBounds.isEmpty) { + final minAndMax = _floor(pixelBounds.min / tileDimension.toDouble()); + bounds = IntegerBounds(minAndMax, minAndMax); } else { - bounds = Bounds( - (pixelBounds.min / tileDimension).floor(), - (pixelBounds.max / tileDimension).ceil() - const Point(1, 1), + bounds = IntegerBounds( + _floor(pixelBounds.min / tileDimension.toDouble()), + _ceil(pixelBounds.max / tileDimension.toDouble()) - const Point(1, 1), ); } @@ -88,7 +96,7 @@ class DiscreteTileRange extends TileRange { return DiscreteTileRange( zoom, - Bounds( + IntegerBounds( Point(math.max(min.x, minX), min.y), Point(math.min(max.x, maxX), max.y), ), @@ -103,7 +111,7 @@ class DiscreteTileRange extends TileRange { return DiscreteTileRange( zoom, - Bounds( + IntegerBounds( Point(min.x, math.max(min.y, minY)), Point(max.x, math.min(max.y, maxY)), ), @@ -145,7 +153,7 @@ class DiscreteTileRange extends TileRange { Point get max => _bounds.max; /// The center [Point] of the [DiscreteTileRange] - Point get center => _bounds.center; + Offset get center => _bounds.center; /// Get a list of [TileCoordinates] for the [DiscreteTileRange]. @override diff --git a/lib/src/layer/tile_layer/tile_range_calculator.dart b/lib/src/layer/tile_layer/tile_range_calculator.dart index 169f11c17..d85a6d572 100644 --- a/lib/src/layer/tile_layer/tile_range_calculator.dart +++ b/lib/src/layer/tile_layer/tile_range_calculator.dart @@ -1,5 +1,8 @@ +import 'dart:ui'; + import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; @@ -37,7 +40,7 @@ class TileRangeCalculator { ); } - Bounds _calculatePixelBounds( + Rect _calculatePixelBounds( MapCamera camera, LatLng center, double viewingZoom, @@ -45,10 +48,12 @@ class TileRangeCalculator { ) { final tileZoomDouble = tileZoom.toDouble(); final scale = camera.getZoomScale(viewingZoom, tileZoomDouble); - final pixelCenter = - camera.project(center, tileZoomDouble).floor().toDoublePoint(); + final pixelCenter = camera.projectAtZoom(center, tileZoomDouble).floor(); final halfSize = camera.size / (scale * 2); - return Bounds(pixelCenter - halfSize, pixelCenter + halfSize); + return Rect.fromPoints( + pixelCenter - halfSize.bottomRight(Offset.zero), + pixelCenter + halfSize.bottomRight(Offset.zero), + ); } } diff --git a/lib/src/layer/tile_layer/wms_tile_layer_options.dart b/lib/src/layer/tile_layer/wms_tile_layer_options.dart index 650012e82..5c4c70de3 100644 --- a/lib/src/layer/tile_layer/wms_tile_layer_options.dart +++ b/lib/src/layer/tile_layer/wms_tile_layer_options.dart @@ -71,14 +71,16 @@ class WMSTileLayerOptions { String getUrl(TileCoordinates coords, int tileDimension, bool retinaMode) { final nwPoint = coords * tileDimension; final sePoint = nwPoint + Point(tileDimension, tileDimension); - final nwCoords = crs.pointToLatLng(nwPoint, coords.z.toDouble()); - final seCoords = crs.pointToLatLng(sePoint, coords.z.toDouble()); + final nwCoords = + crs.offsetToLatLng(nwPoint.toOffset(), coords.z.toDouble()); + final seCoords = + crs.offsetToLatLng(sePoint.toOffset(), coords.z.toDouble()); final nw = crs.projection.project(nwCoords); final se = crs.projection.project(seCoords); - final bounds = Bounds(nw, se); + final bounds = Rect.fromPoints(nw, se); final bbox = (_versionNumber >= 1.3 && crs is Epsg4326) - ? [bounds.min.y, bounds.min.x, bounds.max.y, bounds.max.x] - : [bounds.min.x, bounds.min.y, bounds.max.x, bounds.max.y]; + ? [bounds.min.dy, bounds.min.dx, bounds.max.dy, bounds.max.dx] + : [bounds.min.dx, bounds.min.dy, bounds.max.dx, bounds.max.dy]; final buffer = StringBuffer(_encodedBaseUrl); buffer.write('&width=${retinaMode ? tileDimension * 2 : tileDimension}'); diff --git a/lib/src/map/camera/camera.dart b/lib/src/map/camera/camera.dart index 81853ea09..bef6bc65c 100644 --- a/lib/src/map/camera/camera.dart +++ b/lib/src/map/camera/camera.dart @@ -1,9 +1,9 @@ -import 'dart:math' as math hide Point; -import 'dart:math' show Point; +import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/map/inherited_model.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; import 'package:vector_math/vector_math_64.dart'; @@ -18,7 +18,8 @@ class MapCamera { /// in a subsequent build to the actual constraints. We set the size to this /// impossible (negative) value initially and only change it once Flutter /// provides real constraints. - static const kImpossibleSize = Point(-1, -1); + static const kImpossibleSize = + Size(double.negativeInfinity, double.negativeInfinity); /// The used coordinate reference system final Crs crs; @@ -41,32 +42,32 @@ class MapCamera { /// The size of the map view ignoring rotation. This will be the size of the /// FlutterMap widget. - final Point nonRotatedSize; + final Size nonRotatedSize; /// Lazily calculated field - Point? _cameraSize; + Size? _cameraSize; /// Lazily calculated field - Bounds? _pixelBounds; + Rect? _pixelBounds; /// Lazily calculated field LatLngBounds? _bounds; /// Lazily calculated field - Point? _pixelOrigin; + Offset? _pixelOrigin; /// This is the [LatLngBounds] corresponding to four corners of this camera. /// This takes rotation in to account. LatLngBounds get visibleBounds => _bounds ??= LatLngBounds( - unproject(pixelBounds.bottomLeft, zoom), - unproject(pixelBounds.topRight, zoom), + unprojectAtZoom(pixelBounds.bottomLeft, zoom), + unprojectAtZoom(pixelBounds.topRight, zoom), ); /// The size of bounding box of this camera taking in to account its /// rotation. When the rotation is zero this will equal [nonRotatedSize], /// otherwise it will be the size of the rectangle which contains this /// camera. - Point get size => _cameraSize ??= calculateRotatedSize( + Size get size => _cameraSize ??= calculateRotatedSize( rotation, nonRotatedSize, ); @@ -74,8 +75,8 @@ class MapCamera { /// The offset of the top-left corner of the bounding rectangle of this /// camera. This will not equal the offset of the top-left visible pixel when /// the map is rotated. - Point get pixelOrigin => - _pixelOrigin ??= project(center, zoom) - size / 2.0; + Offset get pixelOrigin => + _pixelOrigin ??= projectAtZoom(center, zoom) - size.center(Offset.zero); /// The camera of the closest [FlutterMap] ancestor. If this is called from a /// context with no [FlutterMap] ancestor null, is returned. @@ -100,10 +101,10 @@ class MapCamera { required this.nonRotatedSize, this.minZoom, this.maxZoom, - Point? size, - Bounds? pixelBounds, + Size? size, + Rect? pixelBounds, LatLngBounds? bounds, - Point? pixelOrigin, + Offset? pixelOrigin, }) : _cameraSize = size, _pixelBounds = pixelBounds, _bounds = bounds, @@ -121,7 +122,7 @@ class MapCamera { nonRotatedSize = kImpossibleSize; /// Returns a new instance of [MapCamera] with the given [nonRotatedSize]. - MapCamera withNonRotatedSize(Point nonRotatedSize) { + MapCamera withNonRotatedSize(Size nonRotatedSize) { if (nonRotatedSize == this.nonRotatedSize) return this; return MapCamera( @@ -208,20 +209,21 @@ class MapCamera { /// Calculates the size of a bounding box which surrounds a box of size /// [nonRotatedSize] which is rotated by [rotation]. - static Point calculateRotatedSize( + static Size calculateRotatedSize( double rotation, - Point nonRotatedSize, + Size nonRotatedSize, ) { if (rotation == 0.0) return nonRotatedSize; final rotationRad = degrees2Radians * rotation; final cosAngle = math.cos(rotationRad).abs(); final sinAngle = math.sin(rotationRad).abs(); - final width = (nonRotatedSize.x * cosAngle) + (nonRotatedSize.y * sinAngle); + final width = + (nonRotatedSize.width * cosAngle) + (nonRotatedSize.height * sinAngle); final height = - (nonRotatedSize.y * cosAngle) + (nonRotatedSize.x * sinAngle); + (nonRotatedSize.height * cosAngle) + (nonRotatedSize.width * sinAngle); - return Point(width, height); + return Size(width, height); } /// The current rotation value in radians @@ -229,16 +231,13 @@ class MapCamera { /// Calculates point value for the given [latlng] using this camera's /// [crs] and [zoom] (or the provided [zoom]). - Point project(LatLng latlng, [double? zoom]) => - crs.latLngToPoint(latlng, zoom ?? this.zoom); + Offset projectAtZoom(LatLng latlng, [double? zoom]) => + crs.latLngToOffset(latlng, zoom ?? this.zoom); /// Calculates the [LatLng] for the given [point] using this camera's /// [crs] and [zoom] (or the provided [zoom]). - LatLng unproject(Point point, [double? zoom]) => - crs.pointToLatLng(point, zoom ?? this.zoom); - - /// Same as the [unproject] function. - LatLng layerPointToLatLng(Point point) => unproject(point); + LatLng unprojectAtZoom(Offset point, [double? zoom]) => + crs.offsetToLatLng(point, zoom ?? this.zoom); /// Calculates the scale for a zoom from [fromZoom] to [toZoom] using this /// camera\s [crs]. @@ -249,43 +248,46 @@ class MapCamera { double getScaleZoom(double scale) => crs.zoom(scale * crs.scale(zoom)); /// Calculates the pixel bounds of this camera's [crs]. - Bounds? getPixelWorldBounds(double? zoom) => + Rect? getPixelWorldBounds(double? zoom) => crs.getProjectedBounds(zoom ?? this.zoom); /// Calculates the [Offset] from the [pos] to this camera's [pixelOrigin]. - Offset getOffsetFromOrigin(LatLng pos) => - (project(pos) - pixelOrigin).toOffset(); + Offset getOffsetFromOrigin(LatLng pos) => projectAtZoom(pos) - pixelOrigin; /// Calculates the pixel origin of this [MapCamera] at the given /// [center]/[zoom]. - Point getNewPixelOrigin(LatLng center, [double? zoom]) { - final halfSize = size / 2.0; - return (project(center, zoom) - halfSize).round(); + Offset getNewPixelOrigin(LatLng center, [double? zoom]) { + return (projectAtZoom(center, zoom) - size.center(Offset.zero)).round(); } /// Calculates the pixel bounds of this [MapCamera]. This value is cached. - Bounds get pixelBounds => + Rect get pixelBounds => _pixelBounds ?? (_pixelBounds = pixelBoundsAtZoom(zoom)); /// Calculates the pixel bounds of this [MapCamera] at the given [zoom]. - Bounds pixelBoundsAtZoom(double zoom) { - var halfSize = size / 2; + Rect pixelBoundsAtZoom(double zoom) { + Size cameraSize = size; if (zoom != this.zoom) { final scale = getZoomScale(this.zoom, zoom); - halfSize = size / (scale * 2); + cameraSize = size / (scale * 2); } - final pixelCenter = project(center, zoom).floor().toDoublePoint(); - return Bounds(pixelCenter - halfSize, pixelCenter + halfSize); + final pixelCenter = projectAtZoom(center, zoom).floor(); + + return Rect.fromCenter( + center: pixelCenter, + width: cameraSize.width, + height: cameraSize.height); } /// This will convert a latLng to a position that we could use with a widget /// outside of FlutterMap layer space. Eg using a Positioned Widget. - Point latLngToScreenPoint(LatLng latLng) { - final nonRotatedPixelOrigin = project(center, zoom) - nonRotatedSize / 2.0; + Offset latLngToScreenOffset(LatLng latLng) { + final nonRotatedPixelOrigin = + projectAtZoom(center, zoom) - nonRotatedSize.center(Offset.zero); - var point = crs.latLngToPoint(latLng, zoom); + var point = crs.latLngToOffset(latLng, zoom); - final mapCenter = crs.latLngToPoint(center, zoom); + final mapCenter = crs.latLngToOffset(center, zoom); if (rotation != 0.0) { point = rotatePoint(mapCenter, point, counterRotation: false); @@ -294,13 +296,11 @@ class MapCamera { return point - nonRotatedPixelOrigin; } - /// Calculate the [LatLng] coordinates for a [localPoint]. - LatLng pointToLatLng(Point localPoint) { - final localPointCenterDistance = Point( - (nonRotatedSize.x / 2) - localPoint.x, - (nonRotatedSize.y / 2) - localPoint.y, - ); - final mapCenter = crs.latLngToPoint(center, zoom); + /// Calculate the [LatLng] coordinates for a [offset]. + LatLng screenOffsetToLatLng(Offset offset) { + final localPointCenterDistance = + nonRotatedSize.center(Offset.zero) - offset; + final mapCenter = crs.latLngToOffset(center, zoom); var point = mapCenter - localPointCenterDistance; @@ -308,28 +308,27 @@ class MapCamera { point = rotatePoint(mapCenter, point); } - return crs.pointToLatLng(point, zoom); + return crs.offsetToLatLng(point, zoom); } /// Sometimes we need to make allowances that a rotation already exists, so /// it needs to be reversed (pointToLatLng), and sometimes we want to use /// the same rotation to create a new position (latLngToScreenpoint). /// counterRotation just makes allowances this for this. - Point rotatePoint( - Point mapCenter, - Point point, { + Offset rotatePoint( + Offset mapCenter, + Offset point, { bool counterRotation = true, }) { + //TODO what is the difference between this and the extension method on Offset.rotate?????!?!?! final counterRotationFactor = counterRotation ? -1 : 1; final m = Matrix4.identity() - ..translate(mapCenter.x, mapCenter.y) + ..translate(mapCenter.dx, mapCenter.dy) ..rotateZ(rotationRad * counterRotationFactor) - ..translate(-mapCenter.x, -mapCenter.y); - - final tp = MatrixUtils.transformPoint(m, point.toOffset()); + ..translate(-mapCenter.dx, -mapCenter.dy); - return Point(tp.dx, tp.dy); + return MatrixUtils.transformPoint(m, point); } /// Clamps the provided [zoom] to the range specified by [minZoom] and @@ -343,25 +342,25 @@ class MapCamera { /// zoom level. If [zoom] is not provided the current zoom level of the /// [MapCamera] gets used. LatLng offsetToCrs(Offset offset, [double? zoom]) { - final focalStartPt = project(center, zoom ?? this.zoom); + final focalStartPt = projectAtZoom(center, zoom ?? this.zoom); final point = - (offset.toPoint() - (nonRotatedSize / 2.0)).rotate(rotationRad); + (offset - nonRotatedSize.center(Offset.zero)).rotate(rotationRad); final newCenterPt = focalStartPt + point; - return unproject(newCenterPt, zoom ?? this.zoom); + return unprojectAtZoom(newCenterPt, zoom ?? this.zoom); } /// Calculate the center point which would keep the same point of the map /// visible at the given [cursorPos] with the zoom set to [zoom]. - LatLng focusedZoomCenter(Point cursorPos, double zoom) { + LatLng focusedZoomCenter(Offset cursorPos, double zoom) { // Calculate offset of mouse cursor from viewport center - final viewCenter = nonRotatedSize / 2; - final offset = (cursorPos - viewCenter).rotate(rotationRad); + final offset = + (cursorPos - nonRotatedSize.center(Offset.zero)).rotate(rotationRad); // Match new center coordinate to mouse cursor position final scale = getZoomScale(zoom, this.zoom); final newOffset = offset * (1.0 - 1.0 / scale); - final mapCenter = project(center); - final newCenter = unproject(mapCenter + newOffset); + final mapCenter = projectAtZoom(center); + final newCenter = unprojectAtZoom(mapCenter + newOffset); return newCenter; } diff --git a/lib/src/map/camera/camera_constraint.dart b/lib/src/map/camera/camera_constraint.dart index c5f632a91..06a4f3cf8 100644 --- a/lib/src/map/camera/camera_constraint.dart +++ b/lib/src/map/camera/camera_constraint.dart @@ -1,5 +1,5 @@ -import 'dart:math' as math hide Point; -import 'dart:math' show Point; +import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; @@ -110,32 +110,32 @@ class ContainCamera extends CameraConstraint { final testZoom = camera.zoom; final testCenter = camera.center; - final nePixel = camera.project(bounds.northEast, testZoom); - final swPixel = camera.project(bounds.southWest, testZoom); + final nePixel = camera.projectAtZoom(bounds.northEast, testZoom); + final swPixel = camera.projectAtZoom(bounds.southWest, testZoom); final halfSize = camera.size / 2; // Find the limits for the map center which would keep the camera within the // [latLngBounds]. - final leftOkCenter = math.min(swPixel.x, nePixel.x) + halfSize.x; - final rightOkCenter = math.max(swPixel.x, nePixel.x) - halfSize.x; - final topOkCenter = math.min(swPixel.y, nePixel.y) + halfSize.y; - final botOkCenter = math.max(swPixel.y, nePixel.y) - halfSize.y; + final leftOkCenter = math.min(swPixel.dx, nePixel.dx) + halfSize.width; + final rightOkCenter = math.max(swPixel.dx, nePixel.dx) - halfSize.width; + final topOkCenter = math.min(swPixel.dy, nePixel.dy) + halfSize.height; + final botOkCenter = math.max(swPixel.dy, nePixel.dy) - halfSize.height; // Stop if we are zoomed out so far that the camera cannot be translated to // stay within [latLngBounds]. if (leftOkCenter > rightOkCenter || topOkCenter > botOkCenter) return null; - final centerPix = camera.project(testCenter, testZoom); - final newCenterPix = Point( - centerPix.x.clamp(leftOkCenter, rightOkCenter), - centerPix.y.clamp(topOkCenter, botOkCenter), + final centerPix = camera.projectAtZoom(testCenter, testZoom); + final newCenterPix = Offset( + centerPix.dx.clamp(leftOkCenter, rightOkCenter), + centerPix.dy.clamp(topOkCenter, botOkCenter), ); if (newCenterPix == centerPix) return camera; return camera.withPosition( - center: camera.unproject(newCenterPix, testZoom), + center: camera.unprojectAtZoom(newCenterPix, testZoom), ); } diff --git a/lib/src/map/camera/camera_fit.dart b/lib/src/map/camera/camera_fit.dart index ca4b31e2c..939c6351f 100644 --- a/lib/src/map/camera/camera_fit.dart +++ b/lib/src/map/camera/camera_fit.dart @@ -1,8 +1,8 @@ -import 'dart:math' as math hide Point; -import 'dart:math' show Point; +import 'dart:math' as math; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:latlong2/latlong.dart'; /// Describes a position for a [MapCamera] @@ -102,8 +102,8 @@ class FitBounds extends CameraFit { /// Returns a new [MapCamera] which fits this classes configuration. @override MapCamera fit(MapCamera camera) { - final paddingTL = Point(padding.left, padding.top); - final paddingBR = Point(padding.right, padding.bottom); + final paddingTL = Offset(padding.left, padding.top); + final paddingBR = Offset(padding.right, padding.bottom); final paddingTotalXY = paddingTL + paddingBR; @@ -111,10 +111,10 @@ class FitBounds extends CameraFit { if (maxZoom != null) newZoom = math.min(maxZoom!, newZoom); final paddingOffset = (paddingBR - paddingTL) / 2; - final swPoint = camera.project(bounds.southWest, newZoom); - final nePoint = camera.project(bounds.northEast, newZoom); + final swPoint = camera.projectAtZoom(bounds.southWest, newZoom); + final nePoint = camera.projectAtZoom(bounds.northEast, newZoom); - final Point projectedCenter; + final Offset projectedCenter; if (camera.rotation != 0.0) { final swPointRotated = swPoint.rotate(-camera.rotationRad); final nePointRotated = nePoint.rotate(-camera.rotationRad); @@ -126,7 +126,7 @@ class FitBounds extends CameraFit { projectedCenter = (swPoint + nePoint) / 2 + paddingOffset; } - final center = camera.unproject(projectedCenter, newZoom); + final center = camera.unprojectAtZoom(projectedCenter, newZoom); return camera.withPosition( center: center, zoom: newZoom, @@ -135,29 +135,29 @@ class FitBounds extends CameraFit { double _getBoundsZoom( MapCamera camera, - Point pixelPadding, + Offset pixelPadding, ) { final nw = bounds.northWest; final se = bounds.southEast; - var size = camera.nonRotatedSize - pixelPadding; + var size = camera.nonRotatedSize - pixelPadding as Size; // Prevent negative size which results in NaN zoom value later on in the calculation - size = Point(math.max(0, size.x), math.max(0, size.y)); - var boundsSize = Bounds( - camera.project(se, camera.zoom), - camera.project(nw, camera.zoom), + + size = Size(math.max(0, size.width), math.max(0, size.height)); + var boundsSize = Rect.fromPoints( + camera.projectAtZoom(se, camera.zoom), + camera.projectAtZoom(nw, camera.zoom), ).size; if (camera.rotation != 0.0) { final cosAngle = math.cos(camera.rotationRad).abs(); final sinAngle = math.sin(camera.rotationRad).abs(); - boundsSize = Point( - (boundsSize.x * cosAngle) + (boundsSize.y * sinAngle), - (boundsSize.y * cosAngle) + (boundsSize.x * sinAngle), + boundsSize = Size( + (boundsSize.width * cosAngle) + (boundsSize.height * sinAngle), + (boundsSize.height * cosAngle) + (boundsSize.width * sinAngle), ); } - final scaleX = size.x / boundsSize.x; - final scaleY = size.y / boundsSize.y; - final scale = math.min(scaleX, scaleY); + final scale = math.min( + size.width / boundsSize.width, size.height / boundsSize.height); var boundsZoom = camera.getScaleZoom(scale); @@ -218,24 +218,24 @@ class FitInsideBounds extends CameraFit { @override MapCamera fit(MapCamera camera) { - final paddingTL = Point(padding.left, padding.top); - final paddingBR = Point(padding.right, padding.bottom); + final paddingTL = Offset(padding.left, padding.top); + final paddingBR = Offset(padding.right, padding.bottom); final paddingTotalXY = paddingTL + paddingBR; final paddingOffset = (paddingBR - paddingTL) / 2; - final cameraSize = camera.nonRotatedSize - paddingTotalXY; + final cameraSize = camera.nonRotatedSize - paddingTotalXY as Size; - final projectedBoundsSize = Bounds( - camera.project(bounds.southEast, camera.zoom), - camera.project(bounds.northWest, camera.zoom), + final projectedBoundsSize = Rect.fromPoints( + camera.projectAtZoom(bounds.southEast, camera.zoom), + camera.projectAtZoom(bounds.northWest, camera.zoom), ).size; final scale = _rectInRotRectScale( angleRad: camera.rotationRad, - smallRectHalfWidth: cameraSize.x / 2.0, - smallRectHalfHeight: cameraSize.y / 2.0, - bigRectHalfWidth: projectedBoundsSize.x / 2.0, - bigRectHalfHeight: projectedBoundsSize.y / 2.0, + smallRectHalfWidth: cameraSize.width / 2.0, + smallRectHalfHeight: cameraSize.height / 2.0, + bigRectHalfWidth: projectedBoundsSize.width / 2.0, + bigRectHalfHeight: projectedBoundsSize.height / 2.0, ); var newZoom = camera.getScaleZoom(1.0 / scale); @@ -269,23 +269,23 @@ class FitInsideBounds extends CameraFit { LatLng _getCenter( MapCamera camera, { required double newZoom, - required Point paddingOffset, + required Offset paddingOffset, }) { if (camera.rotation == 0.0) { - final swPoint = camera.project(bounds.southWest, newZoom); - final nePoint = camera.project(bounds.northEast, newZoom); + final swPoint = camera.projectAtZoom(bounds.southWest, newZoom); + final nePoint = camera.projectAtZoom(bounds.northEast, newZoom); final projectedCenter = (swPoint + nePoint) / 2 + paddingOffset; - final newCenter = camera.unproject(projectedCenter, newZoom); + final newCenter = camera.unprojectAtZoom(projectedCenter, newZoom); return newCenter; } // Handle rotation - final projectedCenter = camera.project(bounds.center, newZoom); + final projectedCenter = camera.projectAtZoom(bounds.center, newZoom); final rotatedCenter = projectedCenter.rotate(-camera.rotationRad); final adjustedCenter = rotatedCenter + paddingOffset; final derotatedAdjustedCenter = adjustedCenter.rotate(camera.rotationRad); - final newCenter = camera.unproject(derotatedAdjustedCenter, newZoom); + final newCenter = camera.unprojectAtZoom(derotatedAdjustedCenter, newZoom); return newCenter; } @@ -426,8 +426,8 @@ class FitCoordinates extends CameraFit { /// Returns a new [MapCamera] which fits this classes configuration. @override MapCamera fit(MapCamera camera) { - final paddingTL = Point(padding.left, padding.top); - final paddingBR = Point(padding.right, padding.bottom); + final paddingTL = Offset(padding.left, padding.top); + final paddingBR = Offset(padding.right, padding.bottom); final paddingTotalXY = paddingTL + paddingBR; @@ -435,12 +435,11 @@ class FitCoordinates extends CameraFit { if (maxZoom != null) newZoom = math.min(maxZoom!, newZoom); final projectedPoints = - coordinates.map((coord) => camera.project(coord, newZoom)); - - final rotatedPoints = - projectedPoints.map((point) => point.rotate(-camera.rotationRad)); + coordinates.map((coord) => camera.projectAtZoom(coord, newZoom)); - final rotatedBounds = Bounds.containing(rotatedPoints); + final rotatedBounds = RectExtension.containing(projectedPoints + .map((point) => point.rotate(-camera.rotationRad)) + .toList()); // Apply padding final paddingOffset = (paddingBR - paddingTL) / 2; @@ -449,7 +448,7 @@ class FitCoordinates extends CameraFit { // Undo the rotation final unrotatedNewCenter = rotatedNewCenter.rotate(camera.rotationRad); - final newCenter = camera.unproject(unrotatedNewCenter, newZoom); + final newCenter = camera.unprojectAtZoom(unrotatedNewCenter, newZoom); return camera.withPosition( center: newCenter, @@ -459,24 +458,25 @@ class FitCoordinates extends CameraFit { double _getCoordinatesZoom( MapCamera camera, - Point pixelPadding, + Offset pixelPadding, ) { - var size = camera.nonRotatedSize - pixelPadding; + var size = camera.nonRotatedSize - pixelPadding as Size; // Prevent negative size which results in NaN zoom value later on in the calculation - size = Point(math.max(0, size.x), math.max(0, size.y)); + size = Size(math.max(0, size.width), math.max(0, size.height)); final projectedPoints = [ - for (final coord in coordinates) camera.project(coord) + for (final coord in coordinates) camera.projectAtZoom(coord) ]; - final rotatedPoints = - projectedPoints.map((point) => point.rotate(-camera.rotationRad)); - final rotatedBounds = Bounds.containing(rotatedPoints); + final rotatedBounds = RectExtension.containing(projectedPoints + .map((point) => point.rotate(-camera.rotationRad)) + .toList()); final boundsSize = rotatedBounds.size; - final scaleX = size.x / boundsSize.x; - final scaleY = size.y / boundsSize.y; + // TODO this could be replaced with Size.shortestSide + final scaleX = size.width / boundsSize.width; + final scaleY = size.height / boundsSize.height; final scale = math.min(scaleX, scaleY); var newZoom = camera.getScaleZoom(scale); diff --git a/lib/src/map/controller/map_controller.dart b/lib/src/map/controller/map_controller.dart index 77d6a77da..30466c3ae 100644 --- a/lib/src/map/controller/map_controller.dart +++ b/lib/src/map/controller/map_controller.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -87,10 +86,6 @@ abstract class MapController { /// /// See [rotate] to rotate the map around the current map center. /// - /// One, and only one, of [point] or [offset] must be defined: - /// * [point]: allows rotation around a screen-based point (in normal logical - /// pixels), where `Offset(0,0)` is the top-left of the map widget, and the - /// bottom right is `Offset(mapWidth, mapHeight)`. /// * [offset]: allows rotation around a screen-based offset (in normal logical /// pixels) from the map's center. For example, `Offset(100, 100)` will mean /// the point is the 100px down & 100px right from the center. @@ -107,7 +102,6 @@ abstract class MapController { /// `true`. MoveAndRotateResult rotateAroundPoint( double degree, { - Point? point, Offset? offset, String? id, }); diff --git a/lib/src/map/controller/map_controller_impl.dart b/lib/src/map/controller/map_controller_impl.dart index e1480c1e4..b0b8ad951 100644 --- a/lib/src/map/controller/map_controller_impl.dart +++ b/lib/src/map/controller/map_controller_impl.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/gestures/map_interactive_viewer.dart'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:flutter_map/src/misc/move_and_rotate_result.dart'; import 'package:latlong2/latlong.dart'; import 'package:vector_math/vector_math_64.dart'; @@ -24,7 +24,7 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> Animation? _flingAnimation; late bool _animationHasGesture; late Offset _animationOffset; - late Point _flingMapCenterStartPoint; + late Offset _flingMapCenterStartPoint; /// Constructor of the [MapController] implementation for internal usage. MapControllerImpl({MapOptions? options, TickerProvider? vsync}) @@ -107,13 +107,11 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> @override MoveAndRotateResult rotateAroundPoint( double degree, { - Point? point, Offset? offset, String? id, }) => rotateAroundPointRaw( degree, - point: point, offset: offset, hasGesture: false, source: MapEventSource.mapController, @@ -152,11 +150,11 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> // Algorithm thanks to https://github.com/tlserver/flutter_map_location_marker LatLng center = newCenter; if (offset != Offset.zero) { - final newPoint = camera.project(newCenter, newZoom); - center = camera.unproject( + final newPoint = camera.projectAtZoom(newCenter, newZoom); + center = camera.unprojectAtZoom( camera.rotatePoint( newPoint, - newPoint - Point(offset.dx, offset.dy), + newPoint - offset, ), newZoom, ); @@ -224,19 +222,11 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> /// the map. MoveAndRotateResult rotateAroundPointRaw( double degree, { - required Point? point, required Offset? offset, required bool hasGesture, required MapEventSource source, String? id, }) { - if (point != null && offset != null) { - throw ArgumentError('Only one of `point` or `offset` may be non-null'); - } - if (point == null && offset == null) { - throw ArgumentError('One of `point` or `offset` must be non-null'); - } - if (degree == camera.rotation) { return const (moveSuccess: false, rotateSuccess: false); } @@ -254,17 +244,14 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> } final rotationDiff = degree - camera.rotation; - final rotationCenter = camera.project(camera.center) + - (point != null - ? (point - (camera.nonRotatedSize / 2.0)) - : Point(offset!.dx, offset.dy)) - .rotate(camera.rotationRad); + final rotationCenter = (camera.projectAtZoom(camera.center) + offset!) + .rotate(camera.rotationRad); return ( moveSuccess: moveRaw( - camera.unproject( + camera.unprojectAtZoom( rotationCenter + - (camera.project(camera.center) - rotationCenter) + (camera.projectAtZoom(camera.center) - rotationCenter) .rotate(degrees2Radians * rotationDiff), ), camera.zoom, @@ -326,7 +313,7 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> /// Set the widget size but don't emit a event to the event system. bool setNonRotatedSizeWithoutEmittingEvent( - Point nonRotatedSize, + Size nonRotatedSize, ) { if (nonRotatedSize != MapCamera.kImpossibleSize && nonRotatedSize != camera.nonRotatedSize) { @@ -386,10 +373,10 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> /// To be called when an ongoing drag movement updates. void dragUpdated(MapEventSource source, Offset offset) { - final oldCenterPt = camera.project(camera.center); + final oldCenterPt = camera.projectAtZoom(camera.center); - final newCenterPt = oldCenterPt + offset.toPoint(); - final newCenter = camera.unproject(newCenterPt); + final newCenterPt = oldCenterPt + offset; + final newCenter = camera.unprojectAtZoom(newCenterPt); moveRaw( newCenter, @@ -659,11 +646,9 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> _animationHasGesture = hasGesture; _animationOffset = offset; - _flingMapCenterStartPoint = camera.project(camera.center); + _flingMapCenterStartPoint = camera.projectAtZoom(camera.center); - final distance = - (Offset.zero & Size(camera.nonRotatedSize.x, camera.nonRotatedSize.y)) - .shortestSide; + final distance = (Offset.zero & camera.nonRotatedSize).shortestSide; _flingAnimation = Tween( begin: begin, @@ -728,9 +713,9 @@ class MapControllerImpl extends ValueNotifier<_MapControllerState> // fling animation if (_flingAnimation != null) { final newCenterPoint = _flingMapCenterStartPoint + - _flingAnimation!.value.toPoint().rotate(camera.rotationRad); + _flingAnimation!.value.rotate(camera.rotationRad); moveRaw( - camera.unproject(newCenterPoint), + camera.unprojectAtZoom(newCenterPoint), camera.zoom, hasGesture: _animationHasGesture, source: MapEventSource.flingAnimationController, diff --git a/lib/src/map/widget.dart b/lib/src/map/widget.dart index a17e58e4a..b1b5bbd70 100644 --- a/lib/src/map/widget.dart +++ b/lib/src/map/widget.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -143,7 +141,7 @@ class _FlutterMapStateContainer extends State } void _updateAndEmitSizeIfConstraintsChanged(BoxConstraints constraints) { - final nonRotatedSize = Point( + final nonRotatedSize = Size( constraints.maxWidth, constraints.maxHeight, ); diff --git a/lib/src/misc/bounds.dart b/lib/src/misc/bounds.dart index 501f3de9f..2a81209e7 100644 --- a/lib/src/misc/bounds.dart +++ b/lib/src/misc/bounds.dart @@ -1,22 +1,25 @@ import 'dart:math' as math hide Point; import 'dart:math' show Point; +import 'dart:ui'; +import 'package:flutter_map/src/misc/extensions.dart'; import 'package:meta/meta.dart'; /// Rectangular bound delimited by orthogonal lines passing through two /// points. @immutable -class Bounds { - /// The edge of the bounds with the minimum x and y coordinate - final Point min; - - /// The edge of the bounds with the maximum x and y coordinate - final Point max; - - /// Create a [Bounds] instance in a safe way. - factory Bounds(Point a, Point b) { - final T minX; - final T maxX; +@internal +class IntegerBounds { + /// inthe edge of the bounds with the minimum x and y coordinate + final Point min; + + /// inthe edge of the bounds with the maximum x and y coordinate + final Point max; + + /// Create a [IntegerBounds] instance in a safe way. + factory IntegerBounds(Point a, Point b) { + final int minX; + final int maxX; if (a.x > b.x) { minX = b.x; maxX = a.x; @@ -24,8 +27,8 @@ class Bounds { minX = a.x; maxX = b.x; } - final T minY; - final T maxY; + final int minY; + final int maxY; if (a.y > b.y) { minY = b.y; maxY = a.y; @@ -33,125 +36,61 @@ class Bounds { minY = a.y; maxY = b.y; } - return Bounds.unsafe(Point(minX, minY), Point(maxX, maxY)); + return IntegerBounds.unsafe(Point(minX, minY), Point(maxX, maxY)); } - /// Create a [Bounds] instance **without** checking if [min] is actually the + /// Create a [IntegerBounds] instance **without** checking if [min] is actually the /// minimum and [max] is actually the maximum. - const Bounds.unsafe(this.min, this.max); - - /// Create a [Bounds] as bounding box of a list of points. - static Bounds containing(Iterable> points) { - var maxX = double.negativeInfinity; - var maxY = double.negativeInfinity; - var minX = double.infinity; - var minY = double.infinity; - - for (final point in points) { - maxX = math.max(point.x, maxX); - minX = math.min(point.x, minX); - maxY = math.max(point.y, maxY); - minY = math.min(point.y, minY); - } - - return Bounds.unsafe(Point(minX, minY), Point(maxX, maxY)); - } + const IntegerBounds.unsafe(this.min, this.max); - /// Creates a new [Bounds] obtained by expanding the current ones with a new + /// Creates a new [IntegerBounds] obtained by expanding the current ones with a new /// point. - Bounds extend(Point point) { - return Bounds.unsafe( + IntegerBounds extend(Point point) { + return IntegerBounds.unsafe( Point(math.min(point.x, min.x), math.min(point.y, min.y)), Point(math.max(point.x, max.x), math.max(point.y, max.y)), ); } - /// This [Bounds] central point. - Point get center => Point( - (min.x + max.x) / 2, - (min.y + max.y) / 2, - ); + /// inthis [IntegerBounds] central point. + Offset get center => (min + max).toOffset() / 2; /// Bottom-Left corner's point. - Point get bottomLeft => Point(min.x, max.y); + Point get bottomLeft => Point(min.x, max.y); - /// Top-Right corner's point. - Point get topRight => Point(max.x, min.y); + /// intop-Right corner's point. + Point get topRight => Point(max.x, min.y); - /// Top-Left corner's point. - Point get topLeft => min; + /// intop-Left corner's point. + Point get topLeft => min; /// Bottom-Right corner's point. - Point get bottomRight => max; + Point get bottomRight => max; /// A point that contains the difference between the point's axis projections. - Point get size { + Point get size { return max - min; } /// Check if a [Point] is inside of the bounds. - bool contains(Point point) { + bool contains(Point point) { return (point.x >= min.x) && (point.x <= max.x) && (point.y >= min.y) && (point.y <= max.y); } - /// Check if an other [Bounds] object is inside of the bounds. - bool containsBounds(Bounds b) { - return (b.min.x >= min.x) && - (b.max.x <= max.x) && - (b.min.y >= min.y) && - (b.max.y <= max.y); - } - - /// Checks if a part of the other [Bounds] is contained in this [Bounds]. - bool containsPartialBounds(Bounds b) { - return (b.min.x <= max.x) && - (b.max.x >= min.x) && - (b.min.y <= max.y) && - (b.max.y >= min.y); - } - - /// Checks if the line between the two coordinates is contained within the - /// [Bounds]. - bool aabbContainsLine(double x1, double y1, double x2, double y2) { - // Completely outside. - if ((x1 <= min.x && x2 <= min.x) || - (y1 <= min.y && y2 <= min.y) || - (x1 >= max.x && x2 >= max.x) || - (y1 >= max.y && y2 >= max.y)) { - return false; - } - - final m = (y2 - y1) / (x2 - x1); - - double y = m * (min.x - x1) + y1; - if (y > min.y && y < max.y) return true; - - y = m * (max.x - x1) + y1; - if (y > min.y && y < max.y) return true; - - double x = (min.y - y1) / m + x1; - if (x > min.x && x < max.x) return true; - - x = (max.y - y1) / m + x1; - if (x > min.x && x < max.x) return true; - - return false; - } - - /// Calculates the intersection of two Bounds. The return value will be null - /// if there is no intersection. The returned bounds may be zero size + /// Calculates the intersection of two Bounds. inthe return value will be null + /// if there is no intersection. inthe returned bounds may be zero size /// (bottomLeft == topRight). - Bounds? intersect(Bounds b) { + IntegerBounds? intersect(IntegerBounds b) { final leftX = math.max(min.x, b.min.x); final rightX = math.min(max.x, b.max.x); final topY = math.max(min.y, b.min.y); final bottomY = math.min(max.y, b.max.y); if (leftX <= rightX && topY <= bottomY) { - return Bounds.unsafe(Point(leftX, topY), Point(rightX, bottomY)); + return IntegerBounds.unsafe(Point(leftX, topY), Point(rightX, bottomY)); } return null; diff --git a/lib/src/misc/extensions.dart b/lib/src/misc/extensions.dart index dfd7a23fc..b6284efe1 100644 --- a/lib/src/misc/extensions.dart +++ b/lib/src/misc/extensions.dart @@ -1,64 +1,91 @@ -import 'dart:math'; +import 'dart:math' as math; import 'dart:ui'; -/// Extension methods for the math.[Point] class -extension PointExtension on Point { - /// Create new [Point] whose [x] and [y] values are divided by the respective - /// values in [point]. - Point unscaleBy(Point point) { - return Point(x / point.x, y / point.y); - } +import 'package:meta/meta.dart'; - /// Create a new [Point] where the [x] and [y] values are divided by [factor]. - Point operator /(num factor) { - return Point(x / factor, y / factor); +/// Extension methods for the [math.Point] class +@internal +extension PointExtension on math.Point { + /// Create a new [math.Point] where the [x] and [y] values are divided by [factor]. + math.Point operator /(num factor) { + return math.Point(x / factor, y / factor); } - /// Create a new [Point] where the [x] and [y] values are rounded to the - /// nearest integer. - Point round() { - return Point(x.round(), y.round()); - } + /// Converts to offset + Offset toOffset() => Offset(x.toDouble(), y.toDouble()); +} - /// Create a new [Point] where the [x] and [y] values are rounded up to the - /// nearest integer. - Point ceil() { - return Point(x.ceil(), y.ceil()); - } +/// Extension methods for [Offset] +@internal +extension OffsetExtension on Offset { + /// Creates a [math.Point] representation of this offset. + math.Point toPoint() => math.Point(dx, dy); - /// Create a new [Point] where the [x] and [y] values are rounded down to the - /// nearest integer. - Point floor() { - return Point(x.floor(), y.floor()); + /// Create a new [Offset] whose [dx] and [dy] values are rotated clockwise by + /// [radians]. + Offset rotate(num radians) { + final cosTheta = math.cos(radians); + final sinTheta = math.sin(radians); + final nx = (cosTheta * dx) + (sinTheta * dy); + final ny = (cosTheta * dy) - (sinTheta * dx); + return Offset(nx, ny); } - /// Create a new [Point] whose [x] and [y] values are rotated clockwise by - /// [radians]. - Point rotate(num radians) { - if (radians != 0.0) { - final cosTheta = cos(radians); - final sinTheta = sin(radians); - final nx = (cosTheta * x) + (sinTheta * y); - final ny = (cosTheta * y) - (sinTheta * x); - - return Point(nx, ny); + /// returns new [Offset] where floorToDouble() is called on [dx] and [dy] independently + Offset floor() => Offset(dx.floorToDouble(), dy.floorToDouble()); + + /// returns new [Offset] where roundToDouble() is called on [dx] and [dy] independently + Offset round() => Offset(dx.roundToDouble(), dy.roundToDouble()); +} + +@internal +extension RectExtension on Rect { + /// Create a [Rect] as bounding box of a list of points. + static Rect containing(List points) { + var maxX = double.negativeInfinity; + var maxY = double.negativeInfinity; + var minX = double.infinity; + var minY = double.infinity; + + for (final point in points) { + maxX = math.max(point.dx, maxX); + minX = math.min(point.dx, minX); + maxY = math.max(point.dy, maxY); + minY = math.min(point.dy, minY); } - return toDoublePoint(); + return Rect.fromPoints(Offset(minX, minY), Offset(maxX, maxY)); } - /// Cast the object to a [Point] object with integer values - Point toIntPoint() => Point(x.toInt(), y.toInt()); + /// Checks if the line between the two coordinates is contained within the + /// [Rect]. + bool aabbContainsLine(double x1, double y1, double x2, double y2) { + // Completely outside. + if ((x1 <= left && x2 <= left) || + (y1 <= top && y2 <= top) || + (x1 >= right && x2 >= right) || + (y1 >= bottom && y2 >= bottom)) { + return false; + } - /// Case the object to a [Point] object with double values - Point toDoublePoint() => Point(x.toDouble(), y.toDouble()); + final m = (y2 - y1) / (x2 - x1); - /// Maps the [Point] to an [Offset]. - Offset toOffset() => Offset(x.toDouble(), y.toDouble()); -} + double y = m * (left - x1) + y1; + if (y > top && y < bottom) return true; -/// Extension methods for [Offset] -extension OffsetToPointExtension on Offset { - /// Creates a [Point] representation of this offset. - Point toPoint() => Point(dx, dy); + y = m * (right - x1) + y1; + if (y > top && y < bottom) return true; + + double x = (top - y1) / m + x1; + if (x > left && x < right) return true; + + x = (bottom - y1) / m + x1; + if (x > left && x < right) return true; + + return false; + } + + Offset get min => topLeft; + + Offset get max => bottomRight; } diff --git a/lib/src/misc/offsets.dart b/lib/src/misc/offsets.dart index 0e6aa1771..372891b6c 100644 --- a/lib/src/misc/offsets.dart +++ b/lib/src/misc/offsets.dart @@ -2,7 +2,6 @@ import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/geo/crs.dart'; -import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart'; /// Calculate the [Offset] for the [LatLng] point. @@ -45,8 +44,8 @@ List getOffsets(MapCamera camera, Offset origin, List points) { List getOffsetsXY({ required MapCamera camera, required Offset origin, - required List points, - List>? holePoints, + required List points, + List>? holePoints, }) { // Critically create as little garbage as possible. This is called on every frame. final crs = camera.crs; @@ -68,13 +67,13 @@ List getOffsetsXY({ worldWidth, -worldWidth, ]; - final halfScreenWidth = camera.size.x / 2; + final halfScreenWidth = camera.size.width / 2; final p = realPoints.elementAt(0); late double result; late double bestX; for (int i = 0; i < addedWidths.length; i++) { final addedWidth = addedWidths[i]; - final (x, _) = crs.transform(p.x + addedWidth, p.y, zoomScale); + final (x, _) = crs.transform(p.dx + addedWidth, p.dy, zoomScale); if (i == 0) { result = addedWidth; bestX = x; @@ -97,7 +96,7 @@ List getOffsetsXY({ final v = List.filled(len, Offset.zero, growable: true); for (int i = 0; i < len; ++i) { final p = realPoints.elementAt(i); - final (x, y) = crs.transform(p.x + addedWorldWidth, p.y, zoomScale); + final (x, y) = crs.transform(p.dx + addedWorldWidth, p.dy, zoomScale); v[i] = Offset(x + ox, y + oy); } return v; @@ -106,7 +105,7 @@ List getOffsetsXY({ final v = List.filled(len, Offset.zero, growable: true); for (int i = 0; i < len; ++i) { final p = realPoints.elementAt(i); - final (x, y) = crs.transform(p.x + addedWorldWidth, p.y, zoomScale); + final (x, y) = crs.transform(p.dx + addedWorldWidth, p.dy, zoomScale); v[i] = Offset(x + ox, y + oy); } return v; diff --git a/lib/src/misc/point_in_polygon.dart b/lib/src/misc/point_in_polygon.dart index 9f0192da0..9a58b0bc3 100644 --- a/lib/src/misc/point_in_polygon.dart +++ b/lib/src/misc/point_in_polygon.dart @@ -1,16 +1,15 @@ -import 'dart:math' as math; import 'dart:ui'; /// Checks whether point [p] is within the specified closed [polygon] /// /// Uses the even-odd algorithm and requires closed loop polygons, i.e. /// `polygon.first == polygon.last`. -bool isPointInPolygon(math.Point p, List polygon) { +bool isPointInPolygon(Offset p, List polygon) { final len = polygon.length; assert(len >= 3, 'not a polygon'); assert(polygon.first == polygon.last, 'polygon not closed'); - final double px = p.x.toDouble(); - final double py = p.y.toDouble(); + final double px = p.dx; + final double py = p.dy; bool isInPolygon = false; for (int i = 0, j = len - 1; i < len; j = i++) { diff --git a/lib/src/misc/simplify.dart b/lib/src/misc/simplify.dart index acde80bc5..e14974cd0 100644 --- a/lib/src/misc/simplify.dart +++ b/lib/src/misc/simplify.dart @@ -2,35 +2,9 @@ // https://github.com/mourner/simplify-js/blob/master/simplify.js import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter_map/src/geo/crs.dart'; -import 'package:meta/meta.dart'; - -/// Internal double-precision point/vector implementation not to be used in publicly. -/// -/// This is an optimization. Vector operations on math.Point tend to incur a 20+x -/// penalty due to virtual function overhead caused by reified generics. -/// -/// Further note that unlike math.Point, members are mutable to allow object reuse/pooling -/// and therefore reduce GC pressure. -@internal -final class DoublePoint { - double x; - double y; - - DoublePoint(this.x, this.y); - - DoublePoint operator -(DoublePoint rhs) => DoublePoint(x - rhs.x, y - rhs.y); - - double distanceSq(DoublePoint rhs) { - final double dx = x - rhs.x; - final double dy = y - rhs.y; - return dx * dx + dy * dy; - } - - @override - String toString() => 'DoublePoint($x, $y)'; -} /// square distance from a point to a segment double getSqSegDist( @@ -65,16 +39,16 @@ double getSqSegDist( /// Alternative algorithm to the Douglas Peucker simplification algorithm. /// /// Might actually be more expensive than DP, which is also better -List simplifyRadialDist( - List points, +List simplifyRadialDist( + List points, double sqTolerance, ) { - DoublePoint prevPoint = points[0]; - final List newPoints = [prevPoint]; - late DoublePoint point; + Offset prevPoint = points[0]; + final List newPoints = [prevPoint]; + late Offset point; for (int i = 1, len = points.length; i < len; i++) { point = points[i]; - if (point.distanceSq(prevPoint) > sqTolerance) { + if ((point - prevPoint).distanceSquared > sqTolerance) { newPoints.add(point); prevPoint = point; } @@ -86,11 +60,11 @@ List simplifyRadialDist( } void _simplifyDPStep( - List points, + List points, final int first, final int last, double sqTolerance, - List simplified, + List simplified, ) { double maxSqDist = sqTolerance; final p0 = points[first]; @@ -99,7 +73,7 @@ void _simplifyDPStep( late int index; for (int i = first + 1; i < last; i++) { final p = points[i]; - final double sqDist = getSqSegDist(p.x, p.y, p0.x, p0.y, p1.x, p1.y); + final double sqDist = getSqSegDist(p.dx, p.dy, p0.dx, p0.dy, p1.dx, p1.dy); if (sqDist > maxSqDist) { index = i; @@ -118,20 +92,20 @@ void _simplifyDPStep( } /// simplification using the Ramer-Douglas-Peucker algorithm -List simplifyDouglasPeucker( - List points, +List simplifyDouglasPeucker( + List points, double sqTolerance, ) { final int last = points.length - 1; - final List simplified = [points[0]]; + final List simplified = [points[0]]; _simplifyDPStep(points, 0, last, sqTolerance, simplified); simplified.add(points[last]); return simplified; } /// Simplify the list of points for better performance. -List simplifyPoints({ - required final List points, +List simplifyPoints({ + required final List points, required double tolerance, required bool highQuality, }) { diff --git a/test/core/bounds_test.dart b/test/core/bounds_test.dart index 51bcd34ba..587517aa9 100644 --- a/test/core/bounds_test.dart +++ b/test/core/bounds_test.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:flutter_test/flutter_test.dart'; import '../helpers/core.dart'; @@ -10,7 +10,7 @@ void main() { test( 'should create bounds with minimum point equal to minimum argument ' 'if maximum argument point is positioned higher', () { - final bounds = Bounds(const Point(1, 2), const Point(3, 4)); + final bounds = IntegerBounds(const Point(1, 2), const Point(3, 4)); expect(bounds.min.x, equals(1.0)); expect(bounds.min.y, equals(2.0)); @@ -19,7 +19,7 @@ void main() { test( 'should create bounds with minimum point equal to maximum argument ' 'if maximum argument point is positioned lower', () { - final bounds = Bounds(const Point(3, 4), const Point(1, 2)); + final bounds = IntegerBounds(const Point(3, 4), const Point(1, 2)); expect(bounds.min.x, equals(1.0)); expect(bounds.min.y, equals(2.0)); @@ -28,7 +28,7 @@ void main() { test( 'should create bounds with maximum point equal to minimum argument ' 'if maximum argument point is positioned lower', () { - final bounds = Bounds(const Point(1, 2), const Point(0.01, 0.02)); + final bounds = IntegerBounds(const Point(1, 2), const Point(0, 0)); expect(bounds.max.x, equals(1.0)); expect(bounds.max.y, equals(2.0)); @@ -37,7 +37,7 @@ void main() { test( 'should create bounds with maximum point equal to maximum argument ' 'if maximum argument point is positioned higher', () { - final bounds = Bounds(const Point(0.01, 0.02), const Point(1, 2)); + final bounds = IntegerBounds(const Point(0, 0), const Point(1, 2)); expect(bounds.max.x, equals(1.0)); expect(bounds.max.y, equals(2.0)); @@ -45,42 +45,38 @@ void main() { test('should get center of bounds as a point with x position', () { expect( - Bounds(Point(5.5, randomDouble()), Point(3.3, randomDouble())) - .center - .x, - equals(4.4)); + IntegerBounds(Point(5, randomInt()), Point(4, randomInt())).center.dx, + equals(4.5)); }); test('should get center of bounds as a point with y position', () { expect( - Bounds(Point(randomDouble(), 3.2), Point(randomDouble(), 6.6)) - .center - .y, - equals(4.9)); + IntegerBounds(Point(randomInt(), 3), Point(randomInt(), 4)).center.dy, + equals(3.5)); }); test( 'should create bounds with size represented as point with position ' 'x based on distance between top left and bottom right corners', () { - final size = Bounds( - Point(1.1, randomDouble()), - Point(3.3, randomDouble()), + final size = IntegerBounds( + Point(1, randomInt()), + Point(3, randomInt()), ).size; // avoid float precision problems - expect(size.x.toStringAsPrecision(2), equals('2.2')); + expect(size.x, equals(2)); }); test( 'should create bounds with size represented as point with position ' 'y based on distance between top left and bottom right corners', () { - final size = Bounds( - Point(randomDouble(), 2.2), - Point(randomDouble(), 5.5), + final size = IntegerBounds( + Point(randomInt(), 2), + Point(randomInt(), 5), ).size; // avoid float precision problems - expect(size.y.toStringAsPrecision(2), equals('3.3')); + expect(size.y, equals(3)); }); group('corners', () { @@ -88,213 +84,165 @@ void main() { 'should create bounds with bottom left corner\'s x position ' 'using minimum point x position', () { expect( - Bounds(Point(2.2, randomDouble()), Point(1.1, randomDouble())) + IntegerBounds(Point(2, randomInt()), Point(1, randomInt())) .bottomLeft .x, - equals(1.1)); + equals(1)); }); test( 'should create bounds with bottom left corner\'s y position ' 'using maximum point y position', () { expect( - Bounds(Point(randomDouble(), 1), Point(randomDouble(), 5.5)) + IntegerBounds(Point(randomInt(), 1), Point(randomInt(), 5)) .bottomLeft .y, - equals(5.5)); + equals(5)); }); test( 'should create bounds with top right corner\'s x position ' 'using maximum point x position', () { expect( - Bounds(Point(1, randomDouble()), Point(8.8, randomDouble())) + IntegerBounds(Point(1, randomInt()), Point(8, randomInt())) .topRight .x, - equals(8.8)); + equals(8)); }); test( 'should create bounds with top right corner\'s y position ' 'using minimum point y position', () { expect( - Bounds(Point(randomDouble(), 9.9), Point(randomDouble(), 100)) + IntegerBounds(Point(randomInt(), 10), Point(randomInt(), 100)) .topRight .y, - equals(9.9)); + equals(10)); }); test( 'should create bounds with top left corner\'s x position ' 'using minimum point x position', () { expect( - Bounds(Point(1.1, randomDouble()), Point(2.2, randomDouble())) + IntegerBounds(Point(1, randomInt()), Point(2, randomInt())) .topLeft .x, - equals(1.1)); + equals(1)); }); test( 'should create bounds with top left corner\'s y position ' 'using minimum point y position', () { expect( - Bounds(Point(randomDouble(), 4.4), Point(randomDouble(), 3.3)) + IntegerBounds(Point(randomInt(), 4), Point(randomInt(), 3)) .topLeft .y, - equals(3.3)); + equals(3)); }); test( 'should create bounds with bottom right corner\'s x position ' 'using maximum point x position', () { expect( - Bounds(Point(5.5, randomDouble()), Point(4.4, randomDouble())) + IntegerBounds(Point(5, randomInt()), Point(4, randomInt())) .bottomRight .x, - equals(5.5)); + equals(5)); }); test( 'should create bounds with bottom right corner\'s y position ' 'using maximum point y position', () { expect( - Bounds(Point(randomDouble(), 101.3), Point(randomDouble(), 101.4)) + IntegerBounds(Point(randomInt(), 101), Point(randomInt(), 102)) .bottomRight .y, - equals(101.4)); + equals(102)); }); }); test('should be convertable to string', () { - expect(Bounds(const Point(1.1, 2.2), const Point(3.3, 4.4)).toString(), - equals('Bounds(Point(1.1, 2.2), Point(3.3, 4.4))')); + expect(IntegerBounds(const Point(1, 2), const Point(3, 4)).toString(), + equals('Bounds(Point(1, 2), Point(3, 4))')); }); group('extend', () { test('should create new bounds with updated minimum x position', () { final bounds = - Bounds(Point(-10.1, randomDouble()), Point(11.1, randomDouble())); - final extendedBounds = bounds.extend(Point(-13.3, randomDouble())); + IntegerBounds(Point(-10, randomInt()), Point(11, randomInt())); + final extendedBounds = bounds.extend(Point(-13, randomInt())); - expect(extendedBounds.min.x, -13.3); + expect(extendedBounds.min.x, -13); }); test('should create new bounds with updated minimum y position', () { final bounds = - Bounds(Point(randomDouble(), 3.5), Point(randomDouble(), 101.3)); - final extendedBounds = bounds.extend(Point(randomDouble(), 2.1)); + IntegerBounds(Point(randomInt(), 3), Point(randomInt(), 101)); + final extendedBounds = bounds.extend(Point(randomInt(), 2)); - expect(extendedBounds.min.y, equals(2.1)); + expect(extendedBounds.min.y, equals(2)); }); test('should create new bounds with updated maximum x position', () { final bounds = - Bounds(Point(4.5, randomDouble()), Point(16.3, randomDouble())); - final extendedBounds = bounds.extend(Point(18.9, randomDouble())); + IntegerBounds(Point(4, randomInt()), Point(16, randomInt())); + final extendedBounds = bounds.extend(Point(19, randomInt())); - expect(extendedBounds.max.x, equals(18.9)); + expect(extendedBounds.max.x, equals(19)); }); test('should create new bounds with updated maximum y position', () { final bounds = - Bounds(Point(randomDouble(), 3.5), Point(randomDouble(), 34.3)); - final extendedBounds = bounds.extend(Point(randomDouble(), 38.3)); + IntegerBounds(Point(randomInt(), 4), Point(randomInt(), 34)); + final extendedBounds = bounds.extend(Point(randomInt(), 38)); - expect(extendedBounds.max.y, equals(38.3)); + expect(extendedBounds.max.y, equals(38)); }); test('should create new bounds and keep existing minimum x position', () { final bounds = - Bounds(Point(-10.1, randomDouble()), Point(11.1, randomDouble())); - final extendedBounds = bounds.extend(Point(-7.7, randomDouble())); + IntegerBounds(Point(-10, randomInt()), Point(11, randomInt())); + final extendedBounds = bounds.extend(Point(-7, randomInt())); expect(extendedBounds.min.x, equals(bounds.min.x)); }); test('should create new bounds and keep existing minimum y position', () { final bounds = - Bounds(Point(randomDouble(), 3.3), Point(randomDouble(), 12.7)); - final extendedBounds = bounds.extend(Point(randomDouble(), 4.4)); + IntegerBounds(Point(randomInt(), 3), Point(randomInt(), 13)); + final extendedBounds = bounds.extend(Point(randomInt(), 4)); expect(extendedBounds.min.y, equals(bounds.min.y)); }); test('should create new bounds and keep existing maximum x position', () { final bounds = - Bounds(Point(-15.5, randomDouble()), Point(25.8, randomDouble())); - final extendedBounds = bounds.extend(Point(25.7, randomDouble())); + IntegerBounds(Point(-15, randomInt()), Point(26, randomInt())); + final extendedBounds = bounds.extend(Point(26, randomInt())); expect(extendedBounds.max.x, equals(bounds.max.x)); }); test('should create new bounds and keep existing maximum y position', () { final bounds = - Bounds(Point(randomDouble(), 0), Point(randomDouble(), 15.5)); - final extendedBounds = bounds.extend(Point(randomDouble(), 15.4)); + IntegerBounds(Point(randomInt(), 0), Point(randomInt(), 16)); + final extendedBounds = bounds.extend(Point(randomInt(), 15)); expect(extendedBounds.max.y, equals(bounds.max.y)); }); }); group('contains', () { - test( - 'should contain compared bounds if they are completely within ' - 'the bounds', () { - final bounds = - Bounds(const Point(101.1, 88.1), const Point(133.1, 60.3)); - - expect( - bounds.containsBounds( - Bounds(const Point(110.1, 77.3), const Point(128.3, 65.5))), - isTrue); - }); - - test( - 'should NOT contain compared bounds if they are NOT completely ' - 'within the bounds', () { - final bounds = - Bounds(const Point(101.1, 88.1), const Point(133.1, 60.3)); - - expect( - bounds.containsBounds( - Bounds(const Point(110.1, 77.3), const Point(133.2, 65.5))), - isFalse); - }); - - test( - 'should contain compared bounds partially if at least one edge ' - 'overlaps within the bounds', () { - final bounds = - Bounds(const Point(101.1, 88.1), const Point(133.1, 60.3)); - - expect( - bounds.containsPartialBounds( - Bounds(const Point(200.22, 60.2), const Point(133.1, 60.3))), - isTrue); - }); - - test( - 'should NOT contain compared bounds partially if not a single edge ' - 'overlaps within the bounds', () { - final bounds = - Bounds(const Point(101.1, 88.1), const Point(133.1, 60.3)); - - expect( - bounds.containsPartialBounds( - Bounds(const Point(200.22, 60.2), const Point(133.2, 60.3))), - isFalse); - }); - test('should contain given point within the bounds', () { expect( - Bounds(const Point(0, 50), const Point(50, 0)) + IntegerBounds(const Point(0, 50), const Point(50, 0)) .contains(const Point(25, 25)), isTrue); }); test('should NOT contain given point within the bounds', () { expect( - Bounds(const Point(0, 50), const Point(50, 0)) + IntegerBounds(const Point(0, 50), const Point(50, 0)) .contains(const Point(51, 51)), isFalse); }); diff --git a/test/layer/tile_layer/tile_bounds/crs_fakes.dart b/test/layer/tile_layer/tile_bounds/crs_fakes.dart index ea7f13f46..821d61efc 100644 --- a/test/layer/tile_layer/tile_bounds/crs_fakes.dart +++ b/test/layer/tile_layer/tile_bounds/crs_fakes.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; @@ -25,8 +25,9 @@ class FakeInfiniteCrs extends Crs { const Epsg3857().untransform(x, y, scale); @override - LatLng pointToLatLng(Point point, double zoom) => throw UnimplementedError(); + LatLng offsetToLatLng(Offset point, double zoom) => + throw UnimplementedError(); @override - Bounds? getProjectedBounds(double zoom) => throw UnimplementedError(); + Rect? getProjectedBounds(double zoom) => throw UnimplementedError(); } diff --git a/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart b/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart index fde35643f..bfc334e61 100644 --- a/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart +++ b/test/layer/tile_layer/tile_bounds/tile_bounds_at_zoom_test.dart @@ -1,26 +1,28 @@ import 'dart:math'; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_bounds/tile_bounds_at_zoom.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:test/test.dart'; void main() { group('TileBoundsAtZoom', () { const hugeCoordinate = TileCoordinates(999999999, 999999999, 0); final hugePoint = - Point(hugeCoordinate.x.toDouble(), hugeCoordinate.y.toDouble()); + Offset(hugeCoordinate.x.toDouble(), hugeCoordinate.y.toDouble()); final tileRangeWithHugeCoordinate = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 1, - pixelBounds: Bounds(hugePoint, hugePoint), + pixelBounds: Rect.fromPoints(hugePoint, hugePoint), ); DiscreteTileRange discreteTileRange( int zoom, int minX, int minY, int maxX, int maxY) => DiscreteTileRange( zoom, - Bounds(Point(minX, minY), Point(maxX, maxY)), + IntegerBounds(Point(minX, minY), Point(maxX, maxY)), ); test('InfiniteTileBoundsAtZoom', () { diff --git a/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart b/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart index 1df422e43..9a96f3da3 100644 --- a/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart +++ b/test/layer/tile_layer/tile_bounds/tile_bounds_test.dart @@ -93,7 +93,7 @@ void main() { tileDimension: 256, latLngBounds: LatLngBounds( const LatLng(0, 0), - crs.pointToLatLng(crs.getProjectedBounds(0)!.max, 0), + crs.offsetToLatLng(crs.getProjectedBounds(0)!.bottomRight, 0), ), ); diff --git a/test/layer/tile_layer/tile_image_view_test.dart b/test/layer/tile_layer/tile_image_view_test.dart index 00e7820a7..99801dfd6 100644 --- a/test/layer/tile_layer/tile_image_view_test.dart +++ b/test/layer/tile_layer/tile_image_view_test.dart @@ -5,6 +5,7 @@ import 'package:flutter/src/scheduler/ticker.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_image_view.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; +import 'package:flutter_map/src/misc/bounds.dart'; import 'package:test/test.dart'; import '../../test_utils/test_tile_image.dart'; @@ -23,7 +24,7 @@ void main() { }) => DiscreteTileRange( zoom, - Bounds(Point(x1, y1), Point(x2, y2)), + IntegerBounds(Point(x1, y1), Point(x2, y2)), ); group('staleTiles', () { diff --git a/test/layer/tile_layer/tile_range_test.dart b/test/layer/tile_layer/tile_range_test.dart index 19a0ec39d..c14a86556 100644 --- a/test/layer/tile_layer/tile_range_test.dart +++ b/test/layer/tile_layer/tile_range_test.dart @@ -1,4 +1,5 @@ import 'dart:math'; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/src/layer/tile_layer/tile_range.dart'; @@ -11,12 +12,12 @@ void main() { final tileRange1 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 1, - pixelBounds: Bounds(const Point(1, 1), const Point(2, 2)), + pixelBounds: Rect.fromPoints(const Offset(1, 1), const Offset(2, 2)), ); final tileRange2 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 1, - pixelBounds: Bounds(const Point(3, 3), const Point(4, 4)), + pixelBounds: Rect.fromPoints(const Offset(3, 3), const Offset(4, 4)), ); final emptyTileRange = tileRange1.intersect(tileRange2); @@ -34,9 +35,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(25, 25), - const Point(25, 25), + pixelBounds: Rect.fromPoints( + const Offset(25, 25), + const Offset(25, 25), ), ); @@ -48,9 +49,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(0, 0), - const Point(0.1, 0.1), + pixelBounds: Rect.fromPoints( + Offset.zero, + const Offset(0.1, 0.1), ), ); @@ -62,9 +63,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(0, 0), - const Point(9.99, 9.99), + pixelBounds: Rect.fromPoints( + Offset.zero, + const Offset(9.99, 9.99), ), ); @@ -76,9 +77,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(19.99, 19.99), - const Point(30.1, 30.1), + pixelBounds: Rect.fromPoints( + const Offset(19.99, 19.99), + const Offset(30.1, 30.1), ), ); @@ -100,9 +101,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(25, 25), - const Point(25, 25), + pixelBounds: Rect.fromPoints( + const Offset(25, 25), + const Offset(25, 25), ), ); @@ -127,18 +128,18 @@ void main() { final tileRange1 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(25, 25), - const Point(25, 25), + pixelBounds: Rect.fromPoints( + const Offset(25, 25), + const Offset(25, 25), ), ); final tileRange2 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(35, 35), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(35, 35), ), ); @@ -153,18 +154,18 @@ void main() { final tileRange1 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(25, 25), - const Point(35, 35), + pixelBounds: Rect.fromPoints( + const Offset(25, 25), + const Offset(35, 35), ), ); final tileRange2 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(45, 45), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(45, 45), ), ); @@ -181,18 +182,18 @@ void main() { final tileRange1 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(25, 25), - const Point(35, 35), + pixelBounds: Rect.fromPoints( + const Offset(25, 25), + const Offset(35, 35), ), ); final tileRange2 = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(15, 15), - const Point(45, 45), + pixelBounds: Rect.fromPoints( + const Offset(15, 15), + const Offset(45, 45), ), ); @@ -210,14 +211,14 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(45, 45), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(45, 45), ), ); - expect(tileRange.min, const Point(3, 3)); - expect(tileRange.max, const Point(4, 4)); + expect(tileRange.min, const Point(3, 3)); + expect(tileRange.max, const Point(4, 4)); }); group('center', () { @@ -225,39 +226,39 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(35, 35), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(35, 35), ), ); - expect(tileRange.center, const Point(3, 3)); + expect(tileRange.center, const Offset(3, 3)); }); test('multiple tiles, even number of tiles', () { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(45, 45), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(45, 45), ), ); - expect(tileRange.center, const Point(3.5, 3.5)); + expect(tileRange.center, const Offset(3.5, 3.5)); }); test('multiple tiles, odd number of tiles', () { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 0, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(55, 55), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(55, 55), ), ); - expect(tileRange.center, const Point(4, 4)); + expect(tileRange.center, const Offset(4, 4)); }); }); @@ -265,9 +266,9 @@ void main() { final tileRange = DiscreteTileRange.fromPixelBounds( zoom: 10, tileDimension: 10, - pixelBounds: Bounds( - const Point(35, 35), - const Point(35, 35), + pixelBounds: Rect.fromPoints( + const Offset(35, 35), + const Offset(35, 35), ), ); diff --git a/test/map/camera/camera_constraint_test.dart b/test/map/camera/camera_constraint_test.dart index ff4a804db..17c3e228c 100644 --- a/test/map/camera/camera_constraint_test.dart +++ b/test/map/camera/camera_constraint_test.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +import 'dart:ui'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -20,7 +20,7 @@ void main() { center: const LatLng(-90, -180), zoom: 1, rotation: 45, - nonRotatedSize: const Point(200, 300), + nonRotatedSize: const Size(200, 300), ); final clamped = mapConstraint.constrain(camera)!; diff --git a/test/misc/point_in_polygon_test.dart b/test/misc/point_in_polygon_test.dart index b9f697125..2e2827570 100644 --- a/test/misc/point_in_polygon_test.dart +++ b/test/misc/point_in_polygon_test.dart @@ -19,22 +19,22 @@ void main() { // Inside points for (final point in makeCircle(32, 0.8, 0.0001)) { - final p = math.Point(point.dx, point.dy); + final p = Offset(point.dx, point.dy); expect(isPointInPolygon(p, circle), isTrue); } // Edge-case: check origin - expect(isPointInPolygon(const math.Point(0, 0), circle), isTrue); + expect(isPointInPolygon(Offset.zero, circle), isTrue); // Outside points: small radius for (final point in makeCircle(32, 1.1, 0.0001)) { - final p = math.Point(point.dx, point.dy); + final p = Offset(point.dx, point.dy); expect(isPointInPolygon(p, circle), isFalse); } // Outside points: large radius for (final point in makeCircle(32, 100000, 0.0001)) { - final p = math.Point(point.dx, point.dy); + final p = Offset(point.dx, point.dy); expect(isPointInPolygon(p, circle), isFalse); } });