diff --git a/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileDecoder.java b/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileDecoder.java
new file mode 100644
index 00000000..13a98d41
--- /dev/null
+++ b/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileDecoder.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 Hannes Janetzek
+ * Copyright 2017 devemux86
+ * Copyright 2019 Gustl22
+ *
+ * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.tiling.source.overpass;
+
+import org.oscim.core.GeometryBuffer.GeometryType;
+import org.oscim.core.MapElement;
+import org.oscim.core.Tag;
+import org.oscim.core.TagSet;
+import org.oscim.core.Tile;
+import org.oscim.core.osm.OsmData;
+import org.oscim.core.osm.OsmElement;
+import org.oscim.core.osm.OsmNode;
+import org.oscim.core.osm.OsmRelation;
+import org.oscim.core.osm.OsmWay;
+import org.oscim.tiling.ITileDataSink;
+import org.oscim.tiling.source.ITileDecoder;
+import org.oscim.tiling.source.mapfile.OSMUtils;
+import org.oscim.utils.overpass.OverpassAPIReader;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.oscim.core.MercatorProjection.latitudeToY;
+import static org.oscim.core.MercatorProjection.longitudeToX;
+
+public class OverpassTileDecoder implements ITileDecoder {
+
+ private final MapElement mMapElement;
+ private ITileDataSink mTileDataSink;
+
+ private double mTileY, mTileX, mTileScale;
+
+ public OverpassTileDecoder() {
+ mMapElement = new MapElement();
+ mMapElement.layer = 5;
+ }
+
+ public synchronized boolean decode(Tile tile, ITileDataSink sink, InputStream is) {
+ mTileDataSink = sink;
+ mTileScale = 1 << tile.zoomLevel;
+ mTileX = tile.tileX / mTileScale;
+ mTileY = tile.tileY / mTileScale;
+ mTileScale *= Tile.SIZE;
+
+ OsmData data;
+ try {
+ OverpassAPIReader reader = new OverpassAPIReader();
+ reader.parse(is);
+ data = reader.getData();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ for (OsmNode element : data.getNodes())
+ parseFeature(element);
+ for (OsmWay element : data.getWays())
+ parseFeature(element);
+ for (OsmRelation element : data.getRelations())
+ parseFeature(element);
+
+ return true;
+ }
+
+ private synchronized void parseFeature(OsmElement element) {
+ if (element.tags == null || element.tags.size() == 0)
+ return;
+
+ synchronized (mMapElement) {
+ mMapElement.clear();
+ mMapElement.tags.clear();
+
+ mMapElement.tags.set(element.tags);
+ //add tag information
+ decodeTags(mMapElement);
+
+ parseGeometry(element);
+
+ if (mMapElement.type == GeometryType.NONE)
+ return;
+
+ mTileDataSink.process(mMapElement);
+ }
+ }
+
+ private void parseGeometry(OsmElement element) {
+ //TODO mulipolygons
+ if (element instanceof OsmWay) {
+ boolean linearFeature = !OSMUtils.isArea(mMapElement);
+ if (linearFeature) {
+ mMapElement.type = GeometryType.LINE;
+ parseLine((OsmWay) element);
+ } else {
+ mMapElement.type = GeometryType.POLY;
+ parsePolygon((OsmWay) element);
+ }
+ } else if (element instanceof OsmNode) {
+ mMapElement.type = GeometryType.POINT;
+ mMapElement.startPoints();
+ parseCoordinate((OsmNode) element);
+ }
+ }
+
+ private void parsePolygon(OsmWay element) {
+ //int ring = 0;
+
+ //for (element.rings) {
+ //if (ring == 0)
+ mMapElement.startPolygon();
+ //else
+ // mMapElement.startHole();
+
+ //ring++;
+ parseCoordSequence(element);
+ removeLastPoint();
+ //}
+ }
+
+ private void removeLastPoint() {
+ mMapElement.pointNextPos -= 2;
+ mMapElement.index[mMapElement.indexCurrentPos] -= 2;
+ }
+
+ private void parseLine(OsmWay element) {
+ mMapElement.startLine();
+ parseCoordSequence(element);
+ }
+
+ private void parseCoordSequence(OsmWay element) {
+ for (OsmNode node : element.nodes)
+ parseCoordinate(node);
+ }
+
+ private void parseCoordinate(OsmNode element) {
+ mMapElement.addPoint((float) ((longitudeToX(element.lon) - mTileX) * mTileScale),
+ (float) ((latitudeToY(element.lat) - mTileY) * mTileScale));
+
+ }
+
+ private void decodeTags(MapElement mapElement) {
+ TagSet tags = mapElement.tags;
+ Tag tag = tags.get(Tag.KEY_ROOF_DIRECTION);
+ if (tag != null) {
+ if (!isNumeric(tag.value)) {
+ switch (tag.value.toLowerCase()) {
+ case "n":
+ case "north":
+ tag.value = "0";
+ break;
+ case "e":
+ case "east":
+ tag.value = "90";
+ break;
+ case "s":
+ case "south":
+ tag.value = "180";
+ break;
+ case "w":
+ case "west":
+ tag.value = "270";
+ break;
+
+ case "ne":
+ tag.value = "45";
+ break;
+ case "se":
+ tag.value = "135";
+ break;
+ case "sw":
+ tag.value = "225";
+ break;
+ case "nw":
+ tag.value = "315";
+ break;
+
+ case "nne":
+ tag.value = "22";
+ break;
+ case "ene":
+ tag.value = "67";
+ break;
+ case "ese":
+ tag.value = "112";
+ break;
+ case "sse":
+ tag.value = "157";
+ break;
+ case "ssw":
+ tag.value = "202";
+ break;
+ case "wsw":
+ tag.value = "247";
+ break;
+ case "wnw":
+ tag.value = "292";
+ break;
+ case "nnw":
+ tag.value = "337";
+ break;
+ }
+ }
+ }
+ }
+
+ private static boolean isNumeric(String str) {
+ try {
+ Float.parseFloat(str);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileSource.java b/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileSource.java
new file mode 100644
index 00000000..dcc9fb9d
--- /dev/null
+++ b/vtm-extras/src/org/oscim/tiling/source/overpass/OverpassTileSource.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 Hannes Janetzek
+ * Copyright 2018 devemux86
+ * Copyright 2019 Gustl22
+ *
+ * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.tiling.source.overpass;
+
+import org.oscim.core.BoundingBox;
+import org.oscim.core.Tile;
+import org.oscim.tiling.ITileDataSource;
+import org.oscim.tiling.OverzoomTileDataSource;
+import org.oscim.tiling.source.UrlTileDataSource;
+import org.oscim.tiling.source.UrlTileSource;
+import org.oscim.utils.overpass.OverpassAPIReader;
+
+public class OverpassTileSource extends UrlTileSource {
+
+ private static final String DEFAULT_URL = "https://www.overpass-api.de/api/interpreter?data=[out:json];";
+ private static final String DEFAULT_PATH = "(node{{bbox}};way{{bbox}};>;);out%20body;";
+
+ public static class Builder> extends UrlTileSource.Builder {
+
+ public Builder() {
+ super(DEFAULT_URL, DEFAULT_PATH);
+ zoomMax(17);
+ }
+
+ @Override
+ public OverpassTileSource build() {
+ return new OverpassTileSource(this);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static Builder> builder() {
+ return new Builder();
+ }
+
+ public OverpassTileSource(Builder> builder) {
+ super(builder);
+
+ setUrlFormatter(new TileUrlFormatter() {
+ @Override
+ public String formatTilePath(UrlTileSource tileSource, Tile tile) {
+ BoundingBox bb = tile.getBoundingBox();
+
+ String query = OverpassAPIReader.query(
+ bb.getMinLongitude(),
+ bb.getMaxLongitude(),
+ bb.getMaxLatitude(),
+ bb.getMinLatitude(),
+ DEFAULT_PATH);
+
+ /*String encoded;
+ try {
+ query = URLEncoder.encode(query, "utf-8");
+ } catch (UnsupportedEncodingException e1) {
+ e1.printStackTrace();
+ return null;
+ }*/
+ return query;
+ }
+ });
+ }
+
+ @Override
+ public ITileDataSource getDataSource() {
+ return new OverzoomTileDataSource(new UrlTileDataSource(this, new OverpassTileDecoder(), getHttpEngine()), mOverZoom);
+ }
+}
diff --git a/vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java b/vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java
index c831d678..9dd6e567 100644
--- a/vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java
+++ b/vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java
@@ -1,6 +1,7 @@
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2016 devemux86
+ * Copyright 2019 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@@ -75,25 +76,36 @@ public class OverpassAPIReader {
private final String query;
+ public OverpassAPIReader() {
+ this.query = null;
+ }
+
/**
* Creates a new instance with the specified geographical coordinates.
- *
- * @param left The longitude marking the left edge of the bounding box.
- * @param right The longitude marking the right edge of the bounding box.
- * @param top The latitude marking the top edge of the bounding box.
- * @param bottom The latitude marking the bottom edge of the bounding box.
- * @param baseUrl (optional) The base url of the server (eg.
- * http://www.openstreetmap.org/api/0.5).
*/
public OverpassAPIReader(final double left, final double right,
- final double top, final double bottom, final String baseUrl,
+ final double top, final double bottom,
final String query) {
+ this.query = query(left, right, top, bottom, query);
+ }
+
+ /**
+ * @param left The longitude marking the left edge of the bounding box.
+ * @param right The longitude marking the right edge of the bounding box.
+ * @param top The latitude marking the top edge of the bounding box.
+ * @param bottom The latitude marking the bottom edge of the bounding box.
+ * @param query The prepared query.
+ * @return the processed query with specified bounding box
+ */
+ public static String query(final double left, final double right,
+ final double top, final double bottom,
+ final String query) {
String bbox = "(" + Math.min(top, bottom) + "," + Math.min(left, right)
+ "," + Math.max(top, bottom) + "," + Math.max(left, right)
+ ")";
- this.query = query.replaceAll("\\{\\{bbox\\}\\}", bbox);
+ return query.replaceAll("\\{\\{bbox\\}\\}", bbox);
}
@@ -335,14 +347,13 @@ public class OverpassAPIReader {
System.out.println(msg);
}
- public OsmData getData() {
-
+ public void parseInputStream() {
String encoded;
try {
encoded = URLEncoder.encode(this.query, "utf-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
- return null;
+ return;
}
System.out.println(myBaseUrl + "?data=" + encoded);
@@ -363,6 +374,9 @@ public class OverpassAPIReader {
}
inputStream = null;
}
+ }
+
+ public OsmData getData() {
for (Entry> entry : relationMembersForRelation
.entrySet()) {
@@ -392,8 +406,8 @@ public class OverpassAPIReader {
}
}
}
- log("nodes: " + ownNodes.size() + " ways: " + ownWays.size()
- + " relations: " + ownRelations.size());
+ /*log("nodes: " + ownNodes.size() + " ways: " + ownWays.size()
+ + " relations: " + ownRelations.size());*/
// give up references to original collections
nodesById = null;
diff --git a/vtm-playground/build.gradle b/vtm-playground/build.gradle
index a0458d78..81b7ced8 100644
--- a/vtm-playground/build.gradle
+++ b/vtm-playground/build.gradle
@@ -5,6 +5,7 @@ dependencies {
file("${rootDir}/vtm-desktop/natives").eachDir() { dir ->
implementation files(dir.path)
}
+ implementation project(':vtm-extras')
implementation project(':vtm-gdx-poi3d')
implementation project(':vtm-http')
implementation project(':vtm-jeo')
diff --git a/vtm-playground/src/org/oscim/test/OverpassTest.java b/vtm-playground/src/org/oscim/test/OverpassTest.java
new file mode 100644
index 00000000..a3a8ce3e
--- /dev/null
+++ b/vtm-playground/src/org/oscim/test/OverpassTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-2018 devemux86
+ * Copyright 2019 Gustl22
+ *
+ * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.test;
+
+import org.oscim.core.MapPosition;
+import org.oscim.gdx.GdxMapApp;
+import org.oscim.layers.GroupLayer;
+import org.oscim.layers.tile.bitmap.BitmapTileLayer;
+import org.oscim.layers.tile.buildings.BuildingLayer;
+import org.oscim.layers.tile.buildings.S3DBLayer;
+import org.oscim.layers.tile.vector.VectorTileLayer;
+import org.oscim.layers.tile.vector.labeling.LabelLayer;
+import org.oscim.map.Map;
+import org.oscim.map.Viewport;
+import org.oscim.theme.VtmThemes;
+import org.oscim.tiling.TileSource;
+import org.oscim.tiling.source.OkHttpEngine;
+import org.oscim.tiling.source.bitmap.DefaultSources;
+import org.oscim.tiling.source.overpass.OverpassTileSource;
+
+/**
+ * Use Overpass API data for vector layer.
+ * Only for developing as can be error-prone.
+ * Take care of overpass provider licenses.
+ */
+public class OverpassTest extends GdxMapApp {
+
+ @Override
+ public void createLayers() {
+ Map map = getMap();
+
+ TileSource tileSource = OverpassTileSource.builder()
+ .httpFactory(new OkHttpEngine.OkHttpFactory())
+ .zoomMin(15)
+ .zoomMax(17)
+ .build();
+ VectorTileLayer l = map.setBaseMap(tileSource);
+
+ TileSource bitmapTileSource = DefaultSources.OPENSTREETMAP
+ .httpFactory(new OkHttpEngine.OkHttpFactory())
+ .zoomMax(15)
+ .fadeSteps(new BitmapTileLayer.FadeStep[]{
+ new BitmapTileLayer.FadeStep(15, 16, 1f, 0f),
+ new BitmapTileLayer.FadeStep(16, Viewport.MAX_ZOOM_LEVEL, 0f, 0f)
+ })
+ .build();
+ mMap.layers().add(new BitmapTileLayer(mMap, bitmapTileSource));
+
+ BuildingLayer.RAW_DATA = true;
+ GroupLayer groupLayer = new GroupLayer(mMap);
+ groupLayer.layers.add(new S3DBLayer(map, l));
+ groupLayer.layers.add(new LabelLayer(map, l));
+ map.layers().add(groupLayer);
+
+ map.setTheme(VtmThemes.DEFAULT);
+ MapPosition pos = MapPreferences.getMapPosition();
+ if (pos != null)
+ map.setMapPosition(pos);
+ else
+ map.setMapPosition(53.075, 8.808, 1 << 17);
+ }
+
+ @Override
+ public void dispose() {
+ MapPreferences.saveMapPosition(mMap.getMapPosition());
+ super.dispose();
+ }
+
+ public static void main(String[] args) {
+ GdxMapApp.init();
+ GdxMapApp.run(new OverpassTest());
+ }
+}
diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
index 4c2dfe75..e3dd4b51 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
@@ -35,6 +35,7 @@ import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.theme.styles.RenderStyle;
+import org.oscim.utils.geom.GeometryUtils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -53,6 +54,11 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
*/
public static boolean POST_AA = false;
+ /**
+ * Use real time calculations to pre-process data.
+ */
+ public static boolean RAW_DATA = false;
+
/**
* Let vanish extrusions / meshes which are covered by others.
* {@link org.oscim.renderer.bucket.RenderBucket#EXTRUSION}: roofs are always translucent.
@@ -147,6 +153,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
mBuildings.put(tile.hashCode(), buildingElements);
}
element = new MapElement(element); // Deep copy, because element will be cleared
+ if (RAW_DATA && element.isClockwise() > 0) {
+ // Buildings must be counter clockwise in VTM (mirrored to OSM)
+ element.reverse();
+ }
buildingElements.add(new BuildingElement(element, extrusion));
return true;
}
@@ -216,8 +226,13 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
// Search buildings which inherit parts
for (BuildingElement rootBuilding : tileBuildings) {
- if (rootBuilding.element.isBuildingPart()
- || !(refId.equals(getValue(rootBuilding.element, Tag.KEY_ID))))
+ if (rootBuilding.element.isBuildingPart())
+ continue;
+ if (RAW_DATA) {
+ float[] center = GeometryUtils.center(partBuilding.element.points, 0, partBuilding.element.pointNextPos, null);
+ if (!GeometryUtils.pointInPoly(center[0], center[1], rootBuilding.element.points, rootBuilding.element.index[0], 0))
+ continue;
+ } else if (!(refId.equals(getValue(rootBuilding.element, Tag.KEY_ID))))
continue;
rootBuildings.add(rootBuilding);
diff --git a/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java b/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
index 77ed8959..9279edca 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/S3DBLayer.java
@@ -27,6 +27,7 @@ import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.map.Map;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.utils.ExtrusionUtils;
+import org.oscim.utils.geom.GeometryUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -191,15 +192,20 @@ public class S3DBLayer extends BuildingLayer {
continue;
String refId = getValue(partBuilding.element, Tag.KEY_REF);
- if (refId == null)
+ if (!RAW_DATA && refId == null)
continue;
TagSet partTags = partBuilding.element.tags;
// Search buildings which inherit parts
for (BuildingElement rootBuilding : tileBuildings) {
- if (rootBuilding.element.isBuildingPart()
- || !(refId.equals(rootBuilding.element.tags.getValue(Tag.KEY_ID))))
+ if (rootBuilding.element.isBuildingPart())
+ continue;
+ if (RAW_DATA) {
+ float[] center = GeometryUtils.center(partBuilding.element.points, 0, partBuilding.element.pointNextPos, null);
+ if (!GeometryUtils.pointInPoly(center[0], center[1], rootBuilding.element.points, rootBuilding.element.index[0], 0))
+ continue;
+ } else if (!refId.equals(rootBuilding.element.tags.getValue(Tag.KEY_ID)))
continue;
if ((getValue(rootBuilding.element, Tag.KEY_ROOF_SHAPE) != null)