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 properties); typedef CircleMarkerCreationCallback = CircleMarker Function( LatLng point, Map properties); typedef PolylineCreationCallback = Polyline Function( List points, Map properties); typedef PolygonCreationCallback = Polygon Function(List points, List>? holePointsList, Map properties); typedef FilterFunction = bool Function(Map 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 markers = []; /// list of [Polyline] objects created as result of parsing final List polylines = []; /// list of [Polygon] objects created as result of parsing final List polygons = []; /// list of [CircleMarker] objects created as result of parsing final List 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)? onMarkerTapCallback; /// user defined callback function called when the [CircleMarker] is tapped void Function(Map)? 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); } /// 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 f) onTapFunction) { onMarkerTapCallback = onTapFunction; } /// set default [CircleMarker] color set setDefaultCircleMarkerColor(Color color) { defaultCircleMarkerColor = color; } /// set default [CircleMarker] tap callback function void setDefaultCircleMarkerTapCallback( Function(Map 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 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)) { 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), ); } break; case 'Circle': { circles.add( circleMarkerCreationCallback!( LatLng(f['geometry']['coordinates'][1] as double, f['geometry']['coordinates'][0] as double), f['properties'] as Map), ); } 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), ); } } break; case 'LineString': { final List 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)); } break; case 'MultiLineString': { for (final line in f['geometry']['coordinates'] as List) { final List 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)); } } break; case 'Polygon': { final List outerRing = []; final List> holesList = []; int pathIndex = 0; for (final path in f['geometry']['coordinates'] as List) { final List hole = []; for (final coords in path as List) { 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)); } break; case 'MultiPolygon': { for (final polygon in f['geometry']['coordinates'] as List) { final List outerRing = []; final List> holesList = []; int pathIndex = 0; for (final path in polygon as List) { List hole = []; for (final coords in path as List) { 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)); } } break; } } return; } /// default function for creating tappable [Marker] Widget defaultTappableMarker(Map properties, void Function(Map) 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 properties) { return Marker( point: point, child: defaultTappableMarker(properties, markerTapped), ); } // /// default callback function for creating [Marker] // Marker createDefaultMarker(LatLng point, Map 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 properties) { return CircleMarker( point: point, radius: properties["radius"].toDouble(), useRadiusInMeter: true, color: defaultCircleMarkerColor!, borderColor: defaultCircleMarkerBorderColor!, ); } /// default callback function for creating [Polyline] Polyline createDefaultPolyline( List points, Map properties) { return Polyline( points: points, color: defaultPolylineColor!, strokeWidth: defaultPolylineStroke!); } /// default callback function for creating [Polygon] Polygon createDefaultPolygon(List outerRing, List>? holesList, Map 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 properties) { return true; } /// default callback function called when tappable [Marker] is tapped void markerTapped(Map map) { if (onMarkerTapCallback != null) { onMarkerTapCallback!(map); } } }