-
Notifications
You must be signed in to change notification settings - Fork 75
Vector layer speed optimization
The most important measure for data size for rendering is number of graphical primitives (points, polyline segments, polygon boundary segments) to be displayed in a given time. When you zoom to whole data extent and try to show hundreds of thousands of segments with full resolution, then it would be slow in any case; and also the resulting image would be quite useless.
Make sure your dataset has spatial indexes. Spatialite has built-in functions for this. For Shapefiles you can use MapServer .qix or ESRI .sbn / .sbx indexes, for shapefile:
ogrinfo buildings.shp -sql "CREATE SPATIAL INDEX ON buildings"
Pre-convert dataset to map view projection, which is usually EPSG:3857 (Google Spherical Mercator), even if SDK Spatialite and OGR layers can actually do live reprojectins which work fine for smaller datasets.
Zoom-based filtering means that you should show only more relevant object classes when user has zoomed out, and more details when user zooms in. For this Nutiteq SDK provides object StyleSet for Points, Markers, Polygons, where different zooms have different Style. If Style is null for given zoom in the styleset, then object is not visible on this zoom. Some samples:
a) Object will be shown in all zoom levels, no zoom-dependent display. Style is given in StyleSet constructor:
Bitmap pointMarker = UnscaledBitmapLoader.decodeResource(getResources(), R.drawable.point);
PointStyle pointStyle = PointStyle.builder().setBitmap(pointMarker).setSize(0.05f).setColor(color).setPickingSize(0.2f).build();
new StyleSet<PointStyle> pointStyleSet = new StyleSet<PointStyle>(pointStyle);
b) Object with same pointStyle is shown from zoom 15:
new StyleSet<PointStyle> pointStyleSet = new StyleSet<PointStyle>();
pointStyleSet.setZoomStyle(15, pointStyle);
c) Object will be shown from zoom 10 to 12
new StyleSet<PointStyle> pointStyleSet = new StyleSet<PointStyle>();
pointStyleSet.setZoomStyle(10, pointStyle);
pointStyleSet.setZoomStyle(13, null); // reset style from zoom 13
Show only data what is relevant for the view, this is also good to make view user-friendly. Make sure to add normal (non-spatial) index to the fields which are used in the filter!
It may be useful to split data, based on display zoom and object classes, or even regions to separate classes. This is always faster than using indexes
DataSources for GIS data (OGR and Spatialite) have method setMaxElements() where you should set upper limit of objects which are shown in any given time. Good number depends on object type: with Point it can be few thousands (say 5000), with polygons and polylines it depends - for complex data it should be smaller (say 1000), for simpler data bigger (say 3000). What really matters is number of object segments, but this counter does not go to this details for sake of speed. This filter takes just first given N objects (rows) from dataset, depending on data layout it could be evenly distributed pseudo-random sample, or something different.
Note that AdvancedMap vector datasource samples have predefined object number limitation, in fact you have to define some upper number there, so if you really need to test your device with millions of objects in single view, just define very big value there.
If you don not need 2.5D view of point objects with Billboard-style display, and view overlapping checks, then use Points. Especially view overlapping checks are expensive, so Point rendering is several times faster, and suggested for bigger datasets than few hundreds of objects.
Spatialite layer has online simplification of data: number of vertexes is reduced automatically, keeping object shapes similar for given zoom level. Negative effect is that after quick zoom in you might notice that you will still see old, simplified object, and after brief moment this will be replaced by another, more detailed line or polygon. Automatic simplification is easy to turn on:
// define pixels and screen width for automatic polygon/line simplification
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// autosimplify with 2 pixel error tolerance, bigger value increases speed but reduces quality
dataSource.setAutoSimplify(2, metrics.widthPixels);
Similar method which makes it even faster, but with the cost of data size increase see next point - pre-simplification.
If you have big datasets (100,000+ objects), especially with very detailed and high-vertex polylines or polygon, then pre-generalization for lower zooms could help a lot. This means that for lower zoom levels you will show one table where data is simplified (generalised), and for more detailed zooms you will show technically different datasource (table in database, or shapefile), which has same data with less or no generalisation. So you have more layers with zoom-dependent display (see above). For pre-generalisation you can use Spatialite built-in st_simplify() function, or for other data formats like Shapefile I would suggest to try ogr2ogr with -simplify flag.
Sometimes you may have really big polygons - hundreds of thousands of vertexes per single polygon. Prime example could be shoreline or whole country boundary. For these it is suggested to split polygon to squares, so you would have many much simpler polygons. If you need to render outline differently, then use original polygon outline, convert it to polyline and render as polyline layer. As example (or for real use) take a look to OpenStreetMap coastline data.
LineStyle has parameter lineJoin, which means how polylines are added together. Nicer line joins are important for wide lines, but they require more GPU rendering processing power. Default line join is BEVEL_LINEJOIN, but if you define lineStyle with simpler NO_LINEJOIN then you may see performance improvement íf there is big number of lines in display. Code:
LineStyle lineStyle = LineStyle.builder()
.setWidth(0.05f)
.setLineJoinMode(LineStyle.NO_LINEJOIN)
.setColor(Color.BLUE)
.build();