MobilApp/lib/controls/geojson_parser.dart

415 lines
14 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
typedef MarkerCreationCallback = Marker Function(
LatLng point, Map<String, dynamic> properties);
typedef CircleMarkerCreationCallback = CircleMarker Function(
LatLng point, Map<String, dynamic> properties);
typedef PolylineCreationCallback = Polyline Function(
List<LatLng> points, Map<String, dynamic> properties);
typedef PolygonCreationCallback = Polygon Function(List<LatLng> points,
List<List<LatLng>>? holePointsList, Map<String, dynamic> properties);
typedef FilterFunction = bool Function(Map<String, dynamic> properties);
/// GeoJsonParser parses the GeoJson and fills three lists of parsed objects
/// which are defined in flutter_map package
/// - list of [Marker]s
/// - list of [CircleMarker]s
/// - list of [Polyline]s
/// - list of [Polygon]s
///
/// One should pass these lists when creating adequate layers in flutter_map.
/// For details see example.
///
/// Currently GeoJson parser supports only FeatureCollection and not GeometryCollection.
/// See the GeoJson Format specification at: https://www.rfc-editor.org/rfc/rfc7946
///
/// For creation of [Marker], [Polyline], [CircleMarker] and [Polygon] objects the default callback functions
/// are provided which are used in case when no user-defined callback function is provided.
/// To fully customize the [Marker], [Polyline], [CircleMarker] and [Polygon] creation one has to write his own
/// callback functions. As a template the default callback functions can be used.
///
class GeoJsonParser {
/// list of [Marker] objects created as result of parsing
final List<Marker> markers = [];
/// list of [Polyline] objects created as result of parsing
final List<Polyline> polylines = [];
/// list of [Polygon] objects created as result of parsing
final List<Polygon> polygons = [];
/// list of [CircleMarker] objects created as result of parsing
final List<CircleMarker> circles = [];
/// user defined callback function that creates a [Marker] object
MarkerCreationCallback? markerCreationCallback;
/// user defined callback function that creates a [Polyline] object
PolylineCreationCallback? polyLineCreationCallback;
/// user defined callback function that creates a [Polygon] object
PolygonCreationCallback? polygonCreationCallback;
/// user defined callback function that creates a [Polygon] object
CircleMarkerCreationCallback? circleMarkerCreationCallback;
/// default [Marker] color
Color? defaultMarkerColor;
/// default [Marker] icon
IconData? defaultMarkerIcon;
/// default [Polyline] color
Color? defaultPolylineColor;
/// default [Polyline] stroke
double? defaultPolylineStroke;
/// default [Polygon] border color
Color? defaultPolygonBorderColor;
/// default [Polygon] fill color
Color? defaultPolygonFillColor;
/// default [Polygon] border stroke
double? defaultPolygonBorderStroke;
/// default flag if [Polygon] is filled (default is true)
bool? defaultPolygonIsFilled;
/// default [CircleMarker] border color
Color? defaultCircleMarkerColor;
/// default [CircleMarker] border stroke
Color? defaultCircleMarkerBorderColor;
/// default flag if [CircleMarker] is filled (default is true)
bool? defaultCircleMarkerIsFilled;
/// user defined callback function called when the [Marker] is tapped
void Function(Map<String, dynamic>)? onMarkerTapCallback;
/// user defined callback function called when the [CircleMarker] is tapped
void Function(Map<String, dynamic>)? onCircleMarkerTapCallback;
/// user defined callback function called during parse for filtering
FilterFunction? filterFunction;
/// default constructor - all parameters are optional and can be set later with setters
GeoJsonParser({
this.markerCreationCallback,
this.polyLineCreationCallback,
this.polygonCreationCallback,
this.circleMarkerCreationCallback,
this.filterFunction,
this.defaultMarkerColor,
this.defaultMarkerIcon,
this.onMarkerTapCallback,
this.defaultPolylineColor,
this.defaultPolylineStroke,
this.defaultPolygonBorderColor,
this.defaultPolygonFillColor,
this.defaultPolygonBorderStroke,
this.defaultPolygonIsFilled,
this.defaultCircleMarkerColor,
this.defaultCircleMarkerBorderColor,
this.defaultCircleMarkerIsFilled,
this.onCircleMarkerTapCallback,
});
/// parse GeJson in [String] format
void parseGeoJsonAsString(String g) {
return parseGeoJson(jsonDecode(g) as Map<String, dynamic>);
}
/// set default [Marker] color
set setDefaultMarkerColor(Color color) {
defaultMarkerColor = color;
}
/// set default [Marker] icon
set setDefaultMarkerIcon(IconData ic) {
defaultMarkerIcon = ic;
}
/// set default [Marker] tap callback function
void setDefaultMarkerTapCallback(
Function(Map<String, dynamic> f) onTapFunction) {
onMarkerTapCallback = onTapFunction;
}
/// set default [CircleMarker] color
set setDefaultCircleMarkerColor(Color color) {
defaultCircleMarkerColor = color;
}
/// set default [CircleMarker] tap callback function
void setDefaultCircleMarkerTapCallback(
Function(Map<String, dynamic> f) onTapFunction) {
onCircleMarkerTapCallback = onTapFunction;
}
/// set default [Polyline] color
set setDefaultPolylineColor(Color color) {
defaultPolylineColor = color;
}
/// set default [Polyline] stroke
set setDefaultPolylineStroke(double stroke) {
defaultPolylineStroke = stroke;
}
/// set default [Polygon] fill color
set setDefaultPolygonFillColor(Color color) {
defaultPolygonFillColor = color;
}
/// set default [Polygon] border stroke
set setDefaultPolygonBorderStroke(double stroke) {
defaultPolygonBorderStroke = stroke;
}
/// set default [Polygon] border color
set setDefaultPolygonBorderColorStroke(Color color) {
defaultPolygonBorderColor = color;
}
/// set default [Polygon] setting whether polygon is filled
set setDefaultPolygonIsFilled(bool filled) {
defaultPolygonIsFilled = filled;
}
/// main GeoJson parsing function
void parseGeoJson(Map<String, dynamic> g) {
// set default values if they are not specified by constructor
markerCreationCallback ??= createDefaultMarker;
circleMarkerCreationCallback ??= createDefaultCircleMarker;
polyLineCreationCallback ??= createDefaultPolyline;
polygonCreationCallback ??= createDefaultPolygon;
filterFunction ??= defaultFilterFunction;
defaultMarkerColor ??= Colors.red.withOpacity(0.8);
defaultMarkerIcon ??= Icons.location_pin;
defaultPolylineColor ??= Colors.blue.withOpacity(0.8);
defaultPolylineStroke ??= 3.0;
defaultPolygonBorderColor ??= Colors.black.withOpacity(0.8);
defaultPolygonFillColor ??= Colors.black.withOpacity(0.1);
defaultPolygonIsFilled ??= true;
defaultPolygonBorderStroke ??= 1.0;
defaultCircleMarkerColor ??= Colors.blue.withOpacity(0.25);
defaultCircleMarkerBorderColor ??= Colors.black.withOpacity(0.8);
defaultCircleMarkerIsFilled ??= true;
// loop through the GeoJson Map and parse it
for (Map f in g['features'] as List) {
String geometryType = f['geometry']['type'].toString();
// check if this spatial object passes the filter function
if (!filterFunction!(f['properties'] as Map<String, dynamic>)) {
continue;
}
switch (geometryType) {
case 'Point':
{
markers.add(
markerCreationCallback!(
LatLng(f['geometry']['coordinates'][1] as double,
f['geometry']['coordinates'][0] as double),
f['properties'] as Map<String, dynamic>),
);
}
break;
case 'Circle':
{
circles.add(
circleMarkerCreationCallback!(
LatLng(f['geometry']['coordinates'][1] as double,
f['geometry']['coordinates'][0] as double),
f['properties'] as Map<String, dynamic>),
);
}
break;
case 'MultiPoint':
{
for (final point in f['geometry']['coordinates'] as List) {
markers.add(
markerCreationCallback!(
LatLng(point[1] as double, point[0] as double),
f['properties'] as Map<String, dynamic>),
);
}
}
break;
case 'LineString':
{
final List<LatLng> lineString = [];
for (final coords in f['geometry']['coordinates'] as List) {
lineString.add(LatLng(coords[1] as double, coords[0] as double));
}
polylines.add(polyLineCreationCallback!(
lineString, f['properties'] as Map<String, dynamic>));
}
break;
case 'MultiLineString':
{
for (final line in f['geometry']['coordinates'] as List) {
final List<LatLng> lineString = [];
for (final coords in line as List) {
lineString
.add(LatLng(coords[1] as double, coords[0] as double));
}
polylines.add(polyLineCreationCallback!(
lineString, f['properties'] as Map<String, dynamic>));
}
}
break;
case 'Polygon':
{
final List<LatLng> outerRing = [];
final List<List<LatLng>> holesList = [];
int pathIndex = 0;
for (final path in f['geometry']['coordinates'] as List) {
final List<LatLng> hole = [];
for (final coords in path as List<dynamic>) {
if (pathIndex == 0) {
// add to polygon's outer ring
outerRing
.add(LatLng(coords[1] as double, coords[0] as double));
} else {
// add it to current hole
hole.add(LatLng(coords[1] as double, coords[0] as double));
}
}
if (pathIndex > 0) {
// add hole to the polygon's list of holes
holesList.add(hole);
}
pathIndex++;
}
polygons.add(polygonCreationCallback!(
outerRing, holesList, f['properties'] as Map<String, dynamic>));
}
break;
case 'MultiPolygon':
{
for (final polygon in f['geometry']['coordinates'] as List) {
final List<LatLng> outerRing = [];
final List<List<LatLng>> holesList = [];
int pathIndex = 0;
for (final path in polygon as List) {
List<LatLng> hole = [];
for (final coords in path as List<dynamic>) {
if (pathIndex == 0) {
// add to polygon's outer ring
outerRing
.add(LatLng(coords[1] as double, coords[0] as double));
} else {
// add it to a hole
hole.add(LatLng(coords[1] as double, coords[0] as double));
}
}
if (pathIndex > 0) {
// add to polygon's list of holes
holesList.add(hole);
}
pathIndex++;
}
polygons.add(polygonCreationCallback!(outerRing, holesList,
f['properties'] as Map<String, dynamic>));
}
}
break;
}
}
return;
}
/// default function for creating tappable [Marker]
Widget defaultTappableMarker(Map<String, dynamic> properties,
void Function(Map<String, dynamic>) onMarkerTap) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
onMarkerTap(properties);
},
child: Icon(defaultMarkerIcon, color: defaultMarkerColor),
),
);
}
/// default callback function for creating [Marker]
Marker createDefaultMarker(LatLng point, Map<String, dynamic> properties) {
return Marker(
point: point,
child: defaultTappableMarker(properties, markerTapped),
);
}
// /// default callback function for creating [Marker]
// Marker createDefaultMarker(LatLng point, Map<String, dynamic> properties) {
// return Marker(
// point: point,
// child: MouseRegion(
// cursor: SystemMouseCursors.click,
// child: GestureDetector(
// onTap: () {
// markerTapped(properties);
// },
// child: Icon(defaultMarkerIcon, color: defaultMarkerColor),
// ),
// ),
// width: 60,
// height: 60,
// );
// }
/// default callback function for creating [Polygon]
CircleMarker createDefaultCircleMarker(
LatLng point, Map<String, dynamic> properties) {
return CircleMarker(
point: point,
radius: properties["radius"].toDouble(),
useRadiusInMeter: true,
color: defaultCircleMarkerColor!,
borderColor: defaultCircleMarkerBorderColor!,
);
}
/// default callback function for creating [Polyline]
Polyline createDefaultPolyline(
List<LatLng> points, Map<String, dynamic> properties) {
return Polyline(
points: points,
color: defaultPolylineColor!,
strokeWidth: defaultPolylineStroke!);
}
/// default callback function for creating [Polygon]
Polygon createDefaultPolygon(List<LatLng> outerRing,
List<List<LatLng>>? holesList, Map<String, dynamic> properties) {
return Polygon(
points: outerRing,
holePointsList: holesList,
borderColor: defaultPolygonBorderColor!,
color: defaultPolygonFillColor!,
// isFilled: defaultPolygonIsFilled!,
borderStrokeWidth: defaultPolygonBorderStroke!,
);
}
/// the default filter function returns always true - therefore no filtering
bool defaultFilterFunction(Map<String, dynamic> properties) {
return true;
}
/// default callback function called when tappable [Marker] is tapped
void markerTapped(Map<String, dynamic> map) {
if (onMarkerTapCallback != null) {
onMarkerTapCallback!(map);
}
}
}