Overpass tile source (#665)
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<T extends Builder<T>> extends UrlTileSource.Builder<T> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<OsmRelation, List<TmpRelation>> 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;
|
||||
|
||||
Reference in New Issue
Block a user