Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVT improvements #2762

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/assets/imagery/imagery_vespucci.geojson
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@
"url": "https://panoramax.openstreetmap.fr/api/map/{z}/{x}/{y}.pbf"
},
"type": "Feature"
},
{
"geometry": null,
"properties": {
"id": "OSMFVECTOR",
"overlay": false,
"max_zoom": 14,
"name": "OSMF Vector Tiles",
"type": "tms",
"tile_type": "mvt",
"category": "osmbasedmap",
"url": "https://vector.openstreetmap.org/shortbread_v1/{z}/{x}/{y}.mvt"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/de/blau/android/dialogs/Layers.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;

import org.mozilla.javascript.RhinoException;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;

import android.annotation.SuppressLint;
Expand Down
96 changes: 89 additions & 7 deletions src/main/java/de/blau/android/util/mvt/VectorTileDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.NoSuchElementException;
import java.util.Set;

import com.mapbox.geojson.CoordinateContainer;
import com.mapbox.geojson.Geometry;
import com.mapbox.geojson.GeometryCollection;
import com.mapbox.geojson.LineString;
Expand All @@ -40,6 +41,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.blau.android.util.GeoJSONConstants;
import vector_tile.VectorTile;
import vector_tile.VectorTile.Tile.GeomType;
import vector_tile.VectorTile.Tile.Layer;
Expand Down Expand Up @@ -262,10 +264,86 @@ static Geometry decodeGeometry(@NonNull GeomType geomType, @NonNull List<Integer
Log.e(DEBUG_TAG, "Empty geometry for " + geomType);
geometry = GeometryCollection.fromGeometries(new ArrayList<>());
}

return geometry;
}

/**
* Calculate the bounding box of a List of Points and sets an existing rect to the values
*
* @param rect the Rect for the result
* @param points the List of Points
*/
private static void rectFromPoints(Rect rect, List<Point> points) {
Point first = points.get(0);
int start = 0;
if (rect.isEmpty()) {
rect.set((int) first.longitude(), (int) first.latitude(), (int) first.longitude(), (int) first.latitude());
start = 1;
}
for (int i = start; i < points.size(); i++) {
Point p = points.get(i);
rect.union((int) p.longitude(), (int) p.latitude());
}
}

/**
* Get a bounding box for a Geometry
*
* @param rect pre-allocated Rect
* @param g the Geometry
* @return the REct set to the bounding box
*/
@NonNull
private static Rect getBoundingBox(@NonNull final Rect rect, @NonNull Geometry g) {
switch (g.type()) {
case GeoJSONConstants.POINT:
rect.union((int) ((Point) g).longitude(), (int) ((Point) g).latitude());
break;
case GeoJSONConstants.MULTIPOINT:
@SuppressWarnings("unchecked")
List<Point> pointList = ((CoordinateContainer<List<Point>>) g).coordinates();
rectFromPoints(rect, pointList);
break;
case GeoJSONConstants.LINESTRING:
@SuppressWarnings("unchecked")
List<Point> line = ((CoordinateContainer<List<Point>>) g).coordinates();
rectFromPoints(rect, line);
break;
case GeoJSONConstants.MULTILINESTRING:
@SuppressWarnings("unchecked")
List<List<Point>> lines = ((CoordinateContainer<List<List<Point>>>) g).coordinates();
for (List<Point> l : lines) {
rectFromPoints(rect, l);
}
break;
case GeoJSONConstants.POLYGON:
@SuppressWarnings("unchecked")
List<List<Point>> rings = ((CoordinateContainer<List<List<Point>>>) g).coordinates();
for (List<Point> ring : rings) {
rectFromPoints(rect, ring);
}
break;
case GeoJSONConstants.MULTIPOLYGON:
@SuppressWarnings("unchecked")
List<List<List<Point>>> polygons = ((CoordinateContainer<List<List<List<Point>>>>) g).coordinates();
for (List<List<Point>> polygon : polygons) {
for (List<Point> ring : polygon) {
rectFromPoints(rect, ring);
}
}
break;
case GeoJSONConstants.GEOMETRYCOLLECTION:
List<Geometry> geometries = ((GeometryCollection) g).geometries();
for (Geometry geometry : geometries) {
getBoundingBox(rect, geometry);
}
break;
default:
Log.e(DEBUG_TAG, "drawGeometry unknown GeoJSON geometry " + g.type());
}
return rect;
}

public static final int COLINEAR = 0;
public static final int CLOCKWISE = -1;
public static final int COUNTERCLOCKWISE = 1;
Expand Down Expand Up @@ -502,19 +580,23 @@ private void parseLayer(@NonNull VectorTile.Tile.Layer layer) {
* @return a Feature
*/
private Feature parseFeature(@NonNull VectorTile.Tile.Feature feature) {

int tagsCount = feature.getTagsCount();
Map<String, Object> attributes = new HashMap<>(tagsCount / 2);
int tagIdx = 0;
while (tagIdx < feature.getTagsCount()) {
while (tagIdx < tagsCount) {
String key = keys.get(feature.getTags(tagIdx++));
Object value = values.get(feature.getTags(tagIdx++));
attributes.put(key, value);
}

Geometry geometry = decodeGeometry(feature.getType(), feature.getGeometryList(), scale);

return new Feature(layerName, extent, geometry, Collections.unmodifiableMap(attributes), feature.getId());
GeomType geomType = feature.getType();
Geometry geometry = decodeGeometry(geomType, feature.getGeometryList(), scale);
Feature f = new Feature(layerName, extent, geometry, Collections.unmodifiableMap(attributes), feature.getId());
if (GeomType.POINT != geomType) {
Rect rect = new Rect();
getBoundingBox(rect, geometry);
f.setBox(rect);
}
return f;
}

@Override
Expand Down
99 changes: 7 additions & 92 deletions src/main/java/de/blau/android/util/mvt/VectorTileRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import java.util.Set;

import com.google.gson.JsonArray;
import com.mapbox.geojson.CoordinateContainer;
import com.mapbox.geojson.Geometry;
import com.mapbox.geojson.GeometryCollection;
import com.mapbox.geojson.Point;

import android.graphics.Canvas;
Expand Down Expand Up @@ -132,8 +130,8 @@ public Layer getLayer(@NonNull String sourceLayer, @NonNull Layer.Type type) {
return null;
}

List<Layer> layerToRender = new ArrayList<>();
List<VectorTileDecoder.Feature> featuresToRender = new ArrayList<>();
private List<Layer> layerToRender = new ArrayList<>();
private List<VectorTileDecoder.Feature> featuresToRender = new ArrayList<>();

@Override
public void preRender(@NonNull Canvas c, int z) {
Expand Down Expand Up @@ -225,12 +223,15 @@ private void setScreenRect(@NonNull Canvas c) {
*/
private void renderFeatures(@NonNull Canvas c, @NonNull Layer layer, int z, @NonNull Rect destinationRect,
@NonNull List<VectorTileDecoder.Feature> features) {

boolean newZoom = z != lastZoom;
for (VectorTileDecoder.Feature feature : features) {
if (z != lastZoom) {
if (newZoom) {
layer.onZoomChange(style, feature, z);
}
layer.render(c, style, feature, z, screenRect, destinationRect, scaleX, scaleX);
}

}

@Override
Expand Down Expand Up @@ -284,25 +285,6 @@ public List<String> getAttributeKeys(@NonNull String layerName) {
return keys == null ? new ArrayList<>() : new ArrayList<>(keys);
}

/**
* Calculate the bounding box of a List of Points and sets an existing rect to the values
*
* @param rect the Rect for the result
* @param points the List of Points
*/
private void rectFromPoints(Rect rect, List<Point> points) {
Point first = points.get(0);
int start = 0;
if (rect.isEmpty()) {
rect.set((int) first.longitude(), (int) first.latitude(), (int) first.longitude(), (int) first.latitude());
start = 1;
}
for (int i = start; i < points.size(); i++) {
Point p = points.get(i);
rect.union((int) p.longitude(), (int) p.latitude());
}
}

/**
* Check if the feature intersects the screen
*
Expand All @@ -317,78 +299,11 @@ private boolean intersectsScreen(@NonNull VectorTileDecoder.Feature f) {
return screenRect.contains(destinationRect.left + (int) (((Point) g).longitude() * scaleX),
destinationRect.top + (int) (((Point) g).latitude() * scaleY));
}
Rect rect = f.getBox();
if (rect == null) {
rect = getBoundingBox(new Rect(), g);
f.setBox(rect);
}
tempRect.set(rect);
tempRect.set(f.getBox());
tempRect.right = destinationRect.left + (int) (tempRect.right * scaleX);
tempRect.left = destinationRect.left + (int) (tempRect.left * scaleX);
tempRect.bottom = destinationRect.top + (int) (tempRect.bottom * scaleY);
tempRect.top = destinationRect.top + (int) (tempRect.top * scaleY);
return tempRect.intersect(screenRect);
}

/**
* Get a bounding box for a Geometry
*
* @param rect pre-allocated Rect
* @param g the Geometry
* @return the REct set to the bounding box
*/
@NonNull
private Rect getBoundingBox(@NonNull Rect rect, @NonNull Geometry g) {
switch (g.type()) {
case GeoJSONConstants.POINT:
rect.union((int) ((Point) g).longitude(), (int) ((Point) g).latitude());
break;
case GeoJSONConstants.MULTIPOINT:
@SuppressWarnings("unchecked")
List<Point> pointList = ((CoordinateContainer<List<Point>>) g).coordinates();
rectFromPoints(rect, pointList);
break;
case GeoJSONConstants.LINESTRING:
@SuppressWarnings("unchecked")
List<Point> line = ((CoordinateContainer<List<Point>>) g).coordinates();
rect = new Rect();
rectFromPoints(rect, line);
break;
case GeoJSONConstants.MULTILINESTRING:
@SuppressWarnings("unchecked")
List<List<Point>> lines = ((CoordinateContainer<List<List<Point>>>) g).coordinates();
rect = new Rect();
for (List<Point> l : lines) {
rectFromPoints(rect, l);
}
break;
case GeoJSONConstants.POLYGON:
@SuppressWarnings("unchecked")
List<List<Point>> rings = ((CoordinateContainer<List<List<Point>>>) g).coordinates();
rect = new Rect();
for (List<Point> ring : rings) {
rectFromPoints(rect, ring);
}
break;
case GeoJSONConstants.MULTIPOLYGON:
@SuppressWarnings("unchecked")
List<List<List<Point>>> polygons = ((CoordinateContainer<List<List<List<Point>>>>) g).coordinates();
rect = new Rect();
for (List<List<Point>> polygon : polygons) {
for (List<Point> ring : polygon) {
rectFromPoints(rect, ring);
}
}
break;
case GeoJSONConstants.GEOMETRYCOLLECTION:
List<Geometry> geometries = ((GeometryCollection) g).geometries();
for (Geometry geometry : geometries) {
getBoundingBox(rect, geometry);
}
break;
default:
Log.e(DEBUG_TAG, "drawGeometry unknown GeoJSON geometry " + g.type());
}
return rect;
}
}
2 changes: 1 addition & 1 deletion src/main/java/de/blau/android/util/mvt/style/Circle.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected void set(int color) {
}
}
};
FloatStyleAttribute strokeOpacity = new FloatStyleAttribute(true) {
FloatStyleAttribute strokeOpacity = new FloatStyleAttribute(false) {
private static final long serialVersionUID = 1L;

@Override
Expand Down
23 changes: 15 additions & 8 deletions src/main/java/de/blau/android/util/mvt/style/Fill.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import androidx.annotation.Nullable;
import de.blau.android.util.GeoJSONConstants;
import de.blau.android.util.SerializableTextPaint;
import de.blau.android.util.collections.FloatPrimitiveList;
import de.blau.android.util.mvt.VectorTileDecoder;
import de.blau.android.util.mvt.VectorTileDecoder.Feature;

Expand All @@ -40,6 +41,8 @@ protected void set(int color) {
};
FloatArrayStyleAttribute fillTranslate = new FloatArrayStyleAttribute(true);

private FloatPrimitiveList points = new FloatPrimitiveList();

/**
* Default constructor
*
Expand Down Expand Up @@ -94,13 +97,13 @@ public void render(Canvas c, Style style, Feature feature, int z, Rect screenRec
case GeoJSONConstants.POLYGON:
@SuppressWarnings("unchecked")
List<List<Point>> rings = ((CoordinateContainer<List<List<Point>>>) g).coordinates();
drawPolygon(c, rings);
drawPolygon(screenRect, c, rings);
break;
case GeoJSONConstants.MULTIPOLYGON:
@SuppressWarnings("unchecked")
List<List<List<Point>>> polygons = ((CoordinateContainer<List<List<List<Point>>>>) g).coordinates();
for (List<List<Point>> polygon : polygons) {
drawPolygon(c, polygon);
drawPolygon(screenRect, c, polygon);
}
break;
default:
Expand All @@ -111,19 +114,23 @@ public void render(Canvas c, Style style, Feature feature, int z, Rect screenRec
/**
* Draw a polygon
*
* @param screenRect screen dimensions
* @param canvas Canvas object we are drawing on
* @param polygon List of List of Point objects defining the polygon rings
*/
private void drawPolygon(@NonNull Canvas canvas, @NonNull List<List<Point>> polygon) {
private void drawPolygon(Rect screenRect, @NonNull Canvas canvas, @NonNull List<List<Point>> polygon) {
path.reset();
float left = destinationRect.left + fillTranslate.literal[0];
float top = destinationRect.top + fillTranslate.literal[1];
for (List<Point> ring : polygon) {
int ringSize = ring.size();
if (ringSize > 2) {
float left = destinationRect.left + fillTranslate.literal[0];
float top = destinationRect.top + fillTranslate.literal[1];
path.moveTo((float) (left + ring.get(0).longitude() * scaleX), (float) (top + ring.get(0).latitude() * scaleY));
for (int i = 1; i < ringSize; i++) {
path.lineTo((float) (left + ring.get(i).longitude() * scaleX), (float) (top + ring.get(i).latitude() * scaleY));
Line.pointListToLinePointsArray(screenRect, left, scaleX, top, scaleY, points, ring);
float[] linePoints = points.getArray();
int pointsSize = points.size();
path.moveTo(linePoints[0], linePoints[1]);
for (int i = 0; i < pointsSize; i = i + 4) {
path.lineTo(linePoints[i + 2], linePoints[i + 3]);
}
path.close();
}
Expand Down
Loading
Loading