Skip to content

Commit

Permalink
Replace CustomPoint with extension methods on Point (#1585)
Browse files Browse the repository at this point in the history
* Replace CustomPoint with extension methods on Point

The CustomPoint class predates dart extension methods. The name is
confusing (custom in what sense, and why) and in looking at the
implementation it became clear that it was essentially just adding
extension methods.

Changing to a Point will avoid conversions in plugins which deal with
libraries that use Point.

Code will work as-is without changes with the exception of the
subtraction operator. Previously CustomPoint<int> - CustomPoint<double>
did not trigger analyzer warnings but would cause a runtime exception.
This is now replaced by subtract() extension methods which allow
subtracting num from a Point<double> and int from Point<int>.

* Avoid redundant maths imports

* Add Point.toOffset extension method

Co-authored-by: 6y <[email protected]>

* Use point extension's toOffset()

---------

Co-authored-by: 6y <[email protected]>
  • Loading branch information
rorystephenson and tlserver authored Jul 20, 2023
1 parent 39962b0 commit 1a17eb1
Show file tree
Hide file tree
Showing 36 changed files with 507 additions and 433 deletions.
61 changes: 43 additions & 18 deletions example/lib/pages/custom_crs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,67 @@

## Projection

To define a `Proj4Crs` (custom CRS) you have to register a projection of `proj4.Projection`. For that you must import `proj4dart` library as follows:
To define a `Proj4Crs` (custom CRS) you have to register a projection of `proj4.Projection`. For
that you must import `proj4dart` library as follows:

```dart
import 'package:proj4dart/proj4dart.dart' as proj4;
```

You can create and register your custom projection in multiple ways, but the recommended is to use a **Proj4 definition string** from [epsg.io](https://epsg.io). For example for `EPSG:3413` (_WGS 84 / NSIDC Sea Ice Polar Stereographic North_) you can find it [here](https://epsg.io/3413.proj4). This is how a Proj4 definition string looks like:
You can create and register your custom projection in multiple ways, but the recommended is to use
a **Proj4 definition string** from [epsg.io](https://epsg.io). For example for `EPSG:3413` (_WGS 84
/ NSIDC Sea Ice Polar Stereographic North_) you can find it [here](https://epsg.io/3413.proj4). This
is how a Proj4 definition string looks like:

```dart
+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84
+
units
=
m
+
no_defs
```

With this **Proj4 definition string** and a **string identifier** register your `proj4.Projection` like this:
With this **Proj4 definition string** and a **string identifier** register your `proj4.Projection`
like this:

```dart
var customProjection = proj4.Projection.add('EPSG:3413',
'+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
'+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
```

For more possible ways to register `proj4.Projection` see [proj4dart documentation](https://github.com/maRci002/proj4dart).
For more possible ways to register `proj4.Projection`
see [proj4dart documentation](https://github.com/maRci002/proj4dart).

## Coordinate Reference System (CRS)

You can use your previously registered `proj4.Projection` to create a custom CRS of type `Proj4Crs`. You can use the following parameters:
You can use your previously registered `proj4.Projection` to create a custom CRS of type `Proj4Crs`.
You can use the following parameters:

- `<String>` `code` (required): string identifier for the selected CRS, e.g. `EPSG:3413`
- `<proj4.Projection>` `proj4Projection` (required): the `proj4.Projection` object you wish to use
- `<Bounds>` `bounds`: bounds of the CRS in projected coordinates
- `<List<double>>` `resolutions`: an array of zoom factors (projection units per pixel, eg. meters/pixel)
- `<List<double>>` `scales`: scale factors (pixels per projection unit); specify either scales or resolutions, but not both!
- `<List<CustomPoint>>` `origins`: tile origin in projected coordinates (for TileLayer). Why is it needed? GeoServer by default can define different origins (top left coordinates) for each zoom levels. In case of origin mismatch the tile will be drawn on the wrong place: the map will jump at such zoom levels. If your origins vary with zoom levels the number of origins must match the number of resolutions. You can get the desired origins from a `GetCapabilities` WMTS call from geoserver e.g. `http://[ip:port]/geoserver/gwc/service/wmts?request=GetCapabilities`. This results an XML, and you have to look up for the `TopLeftCorner`s for each TileMatrix of your TileMatrixSet.
![Tile Origins](./origins.png)
- `<Transformation>` `transformation`: the transformation to use when transforming projected coordinates into pixel coordinates
- `<List<double>>` `resolutions`: an array of zoom factors (projection units per pixel, eg.
meters/pixel)
- `<List<double>>` `scales`: scale factors (pixels per projection unit); specify either scales or
resolutions, but not both!
- `<List<Point>>` `origins`: tile origin in projected coordinates (for TileLayer). Why is it needed?
GeoServer by default can define different origins (top left coordinates) for each zoom levels. In
case of origin mismatch the tile will be drawn on the wrong place: the map will jump at such zoom
levels. If your origins vary with zoom levels the number of origins must match the number of
resolutions. You can get the desired origins from a `GetCapabilities` WMTS call from geoserver
e.g. `http://[ip:port]/geoserver/gwc/service/wmts?request=GetCapabilities`. This results an XML,
and you have to look up for the `TopLeftCorner`s for each TileMatrix of your TileMatrixSet.
![Tile Origins](./origins.png)
- `<Transformation>` `transformation`: the transformation to use when transforming projected
coordinates into pixel coordinates

An example:

```dart
var epsg3413 = proj4.Projection.add('EPSG:3413',
'+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
Expand All @@ -55,8 +79,8 @@ final resolutions = <double>[
];
final epsg3413Bounds = Bounds<double>(
CustomPoint<double>(-4511619.0, -4511336.0),
CustomPoint<double>(4510883.0, 4510996.0),
Point<double>(-4511619.0, -4511336.0),
Point<double>(4510883.0, 4510996.0),
);
var maxZoom = (resolutions.length - 1).toDouble();
Expand All @@ -66,7 +90,7 @@ var epsg3413CRS = Proj4Crs.fromFactory(
proj4Projection: epsg3413,
resolutions: resolutions,
bounds: epsg3413Bounds,
origins: [CustomPoint(0, 0)],
origins: [Point(0, 0)],
scales: null,
transformation: null,
);
Expand All @@ -77,7 +101,7 @@ var epsg3413CRS = Proj4Crs.fromFactory(
Proj4Crs has multiple uses:

- Set `FlutterMap`'s default CRS:

```dart
FlutterMap(
options: MapOptions(
Expand All @@ -92,7 +116,7 @@ Proj4Crs has multiple uses:
```

- Set a WMS layer's CRS

```dart
TileLayerOptions(
opacity: 1.0,
Expand All @@ -109,6 +133,7 @@ Proj4Crs has multiple uses:
);
```

For complete code (with point transformation from one projection to another) see the page source code. This is how it looks like:
For complete code (with point transformation from one projection to another) see the page source
code. This is how it looks like:

![Custom CRS](./custom_crs.png)
10 changes: 6 additions & 4 deletions example/lib/pages/custom_crs/custom_crs.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_map_example/widgets/drawer.dart';
Expand Down Expand Up @@ -56,8 +58,8 @@ class _CustomCrsPageState extends State<CustomCrsPage> {
];

final epsg3413Bounds = Bounds<double>(
const CustomPoint<double>(-4511619, -4511336),
const CustomPoint<double>(4510883, 4510996),
const Point<double>(-4511619, -4511336),
const Point<double>(4510883, 4510996),
);

maxZoom = (resolutions.length - 1).toDouble();
Expand All @@ -76,9 +78,9 @@ class _CustomCrsPageState extends State<CustomCrsPage> {
bounds: epsg3413Bounds,
// Tile origin, in projected coordinates, if set, this overrides the transformation option
// Some goeserver changes origin based on zoom level
// and some are not at all (use explicit/implicit null or use [CustomPoint(0, 0)])
// and some are not at all (use explicit/implicit null or use [Point(0, 0)])
// @see https://github.com/kartena/Proj4Leaflet/pull/171
origins: [const CustomPoint(0, 0)],
origins: [const Point(0, 0)],
// Scale factors (pixels per projection unit, for example pixels/meter) for zoom levels;
// specify either scales or resolutions, not both
scales: null,
Expand Down
8 changes: 5 additions & 3 deletions example/lib/pages/epsg3413_crs.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_map_example/widgets/drawer.dart';
Expand Down Expand Up @@ -37,8 +39,8 @@ class _EPSG3413PageState extends State<EPSG3413Page> {
];

final epsg3413Bounds = Bounds<double>(
const CustomPoint<double>(-4511619, -4511336),
const CustomPoint<double>(4510883, 4510996),
const Point<double>(-4511619, -4511336),
const Point<double>(4510883, 4510996),
);

maxZoom = (resolutions.length - 1).toDouble();
Expand All @@ -55,7 +57,7 @@ class _EPSG3413PageState extends State<EPSG3413Page> {
proj4Projection: epsg3413,
resolutions: resolutions,
bounds: epsg3413Bounds,
origins: [const CustomPoint(0, 0)],
origins: [const Point(0, 0)],
scales: null,
transformation: null,
);
Expand Down
6 changes: 4 additions & 2 deletions example/lib/pages/latlng_to_screen_point.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart';
Expand All @@ -18,7 +20,7 @@ class LatLngScreenPointTestPage extends StatefulWidget {
class _LatLngScreenPointTestPageState extends State<LatLngScreenPointTestPage> {
late final MapController _mapController;

CustomPoint<double> _textPos = const CustomPoint(10, 10);
Point<double> _textPos = const Point(10, 10);

@override
void initState() {
Expand Down Expand Up @@ -48,7 +50,7 @@ class _LatLngScreenPointTestPageState extends State<LatLngScreenPointTestPage> {
onMapEvent: onMapEvent,
onTap: (tapPos, latLng) {
final pt1 = _mapController.camera.latLngToScreenPoint(latLng);
_textPos = CustomPoint(pt1.x, pt1.y);
_textPos = Point(pt1.x, pt1.y);
setState(() {});
},
initialCenter: const LatLng(51.5, -0.09),
Expand Down
4 changes: 3 additions & 1 deletion example/lib/pages/point_to_latlng.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_example/widgets/drawer.dart';
Expand Down Expand Up @@ -106,7 +108,7 @@ class PointToLatlngPage extends State<PointToLatLngPage> {
void updatePoint(MapEvent? event, BuildContext context) {
final pointX = _getPointX(context);
setState(() {
latLng = mapController.camera.pointToLatLng(CustomPoint(pointX, pointY));
latLng = mapController.camera.pointToLatLng(Point(pointX, pointY));
});
}

Expand Down
2 changes: 1 addition & 1 deletion lib/flutter_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ export 'package:flutter_map/src/map/widget.dart';
export 'package:flutter_map/src/misc/center_zoom.dart';
export 'package:flutter_map/src/misc/fit_bounds_options.dart';
export 'package:flutter_map/src/misc/move_and_rotate_result.dart';
export 'package:flutter_map/src/misc/point.dart';
export 'package:flutter_map/src/misc/point_extensions.dart';
export 'package:flutter_map/src/misc/position.dart';
export 'package:flutter_map/src/misc/private/positioned_tap_detector_2.dart';
Loading

0 comments on commit 1a17eb1

Please sign in to comment.