Flutter_map: Slow with many layers

Created on 12 Nov 2018  路  11Comments  路  Source: fleaflet/flutter_map

I just tried the idea from: https://github.com/apptreesoftware/flutter_map/issues/158. The problem is, that i have very large geojson files. Which means a lot of PolygonLayer, which is very unperformant on scrolling. Do you have any idea how to solve this problem?

I have two but no idea if this is possible, because you kown the plugin better then me:

  1. Render only polygons who are currently in the viewport
  2. Reduce the points of polygons if points are very narrow to each other. (Use the midpoint of those two points)

Any suggestions?

question

Most helpful comment

Bounds.contains() in src/core/bounds.dart has an example of how you can detect if a point is in a given bounds

All 11 comments

Both of those sound like good ideas. You could use an intersection method to determine which points are in the viewport

Do you have an example bit of code + json which highlights the problem, would be quite interested to see performance, as there's some similarities to what I may be doing before long.

Yes of course. You need a little bit refactoring, but this is the code i used to test this. The geojson files are the following:

Geojson 1: https://github.com/AshKyd/geojson-regions/blob/master/countries/110m/all.geojson.
Geojson 2: https://github.com/AshKyd/geojson-regions/blob/master/countries/10m/all.geojson

With Geojson 1 performance is bad.
With Geojson 2 performance is very very bad.

I would pay you money if you get Geojson 2 running smothly.

import 'dart:convert';
import 'dart:ui';

import 'package:flutter_map/flutter_map.dart';
import 'package:latlong/latlong.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:flutter/services.dart' show rootBundle;

class MapModel extends Model {
  List<LayerModle> layer = List<LayerModle>();
  MapModel() {
    rebuild();
  }

  void rebuild() {
    rootBundle.loadString('assets/countries/all.geojson').then((result) {
      layer = new List<LayerModle>();
      Map<String, dynamic> map = json.decode(result);
      var features = map["features"] as List<dynamic>;
      features.cast<Map<String, dynamic>>().forEach((feature) {
        var geo = feature["geometry"] as Map<String, dynamic>;
        var type = geo["type"] as String;
        var layers = geo["coordinates"] as List<dynamic>;

        if (type == "Polygon")
          layer.add(LayerModle(layers.cast<List<dynamic>>().map((polygons) {
            return Polygon(
                color: Color.fromRGBO(255, 189, 0, 0.3),
                points: polygons.cast<List<dynamic>>().map((polygon) {
                  return getPosition(polygon);
                }).toList());
          }).toList()));
        else {
          layer.addAll(layers.cast<List<dynamic>>().map((layer) {
            return LayerModle(layer.cast<List<dynamic>>().map((polygons) {
              return Polygon(
                  color: Color.fromRGBO(255, 189, 0, 0.3),
                  points: polygons.cast<List<dynamic>>().map((polygon) {
                    return getPosition(polygon);
                  }).toList());
            }).toList());
          }).toList());
        }
      });
    });
  }

  LatLng getPosition(List polygon) {
    double lat = polygon[1] + .0;
    double long = polygon[0] + .0;
    if (long > 180.0) long = 180.0;
    return LatLng(lat, long);
  }
}

class LayerModle {
  List<Polygon> polygons = List<Polygon>();
  LayerModle(this.polygons);
}

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:hapi/models/map_model.dart';
import 'package:latlong/latlong.dart';
import 'package:scoped_model/scoped_model.dart';

class MapPage extends StatefulWidget {
  @override
  _MapPageState createState() => _MapPageState();
}

class _MapPageState extends State<MapPage> {
  MapModel model = MapModel();
  @override
  Widget build(BuildContext context) {
    return ScopedModel<MapModel>(
        model: model,
        child: ScopedModelDescendant<MapModel>(
          builder: (context, child, model) {
            var layer = List<LayerOptions>();
            layer.add(
              TileLayerOptions(
                urlTemplate: "https://api.tiles.mapbox.com/v4/"
                    "{id}/{z}/{x}/{y}@2x.png?access_token={accessToken}",
                additionalOptions: {
                  'accessToken':
                      'pk.eyJ1IjoibmlrbGFzcmFhYiIsImEiOiJjam8xbGVkZ2MwY3hlM2txaXF1eWJ1ZWdtIn0.IVsKgZVSzR6qJSDAmNhGKA',
                  'id': 'mapbox.dark',
                },
              ),
            );
            layer.addAll(model.layer.map((layer) {
              return PolygonLayerOptions(polygons: layer.polygons);
            }));
            return Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("Rebuild"),
                  onPressed: () {
                    setState(() {
                      model.rebuild();
                    });
                  },
                ),
                Container(
                  height: 350.0,
                  child: FlutterMap(
                    options: MapOptions(
                        center: LatLng(37.422, -122.084), zoom: 10.0),
                    layers: layer,
                  ),
                ),
              ],
            );
          },
        ));
  }
}

@ibrierley Please notify us if you have a performant solution, because i cannot implement the ideas I described, because for this implementations i need to be better. I have no idea how to start on this.

The Webview on: https://github.com/AshKyd/geojson-regions/blob/master/countries/10m/all.geojson , is very performant, you can see, if you drag the map and move it. You can see my ideas from above are implemented. Same for zooming, so it should be possible to do this.

@johnpryan Could you explain this " intersection method" a little bit more detailed? I researched, but found nothing, also i don't understand anything :). Sorry

Bounds.contains() in src/core/bounds.dart has an example of how you can detect if a point is in a given bounds

@johnpryan Thanks, that helped a Lot, tomorrwow i will start on this. Another Question: Where to hook a callback who is called AFTER a camara movement. This method do i need to start the algorithm!

@johnpryan Just tried to hook into onScaleEnd of the GestureDetector, but this method is called multiple times. I cannot recalculate in the onScaleUpdate, because the recalculation would take to long. I have to hook into a event, after the camera movement is done, recalculate the polygons and then redraw them.

This would be great: https://bost.ocks.org/mike/simplify/

(You can move your curser from left to right over the map, and the complexity of the polygon changes)

This is a great discussion, but I don't see any requested changes to the library so I'm going to close this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

reidterror picture reidterror  路  3Comments

vinicentus picture vinicentus  路  3Comments

garrrettt picture garrrettt  路  3Comments

palicka picture palicka  路  4Comments

aytunch picture aytunch  路  4Comments