* Mapsforge Reverse Geocoding #383 * Mapsforge Reverse Geocoding example #383
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2012, 2013 Hannes Janetzek
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@@ -158,6 +159,10 @@ public class MapTile extends Tile {
|
||||
}
|
||||
}
|
||||
|
||||
public MapTile(int tileX, int tileY, int zoomLevel) {
|
||||
this(null, tileX, tileY, zoomLevel);
|
||||
}
|
||||
|
||||
public MapTile(TileNode node, int tileX, int tileY, int zoomLevel) {
|
||||
super(tileX, tileY, (byte) zoomLevel);
|
||||
this.x = (double) tileX / (1 << zoomLevel);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013, 2014 Hannes Janetzek
|
||||
* Copyright 2014-2015 Ludwig M Brinckmann
|
||||
* Copyright 2016-2017 devemux86
|
||||
* Copyright 2016 Andrey Novikov
|
||||
*
|
||||
@@ -20,6 +21,8 @@
|
||||
package org.oscim.tiling.source.mapfile;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
||||
import org.oscim.core.MapElement;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
@@ -35,6 +38,9 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.oscim.core.GeometryBuffer.GeometryType.LINE;
|
||||
import static org.oscim.core.GeometryBuffer.GeometryType.POLY;
|
||||
@@ -167,6 +173,18 @@ public class MapDatabase implements ITileDataSource {
|
||||
*/
|
||||
private static final int WAY_NUMBER_OF_TAGS_BITMASK = 0x0f;
|
||||
|
||||
/**
|
||||
* Way filtering reduces the number of ways returned to only those that are
|
||||
* relevant for the tile requested, leading to performance gains, but can
|
||||
* cause line clipping artifacts (particularly at higher zoom levels). The
|
||||
* risk of clipping can be reduced by either turning way filtering off or by
|
||||
* increasing the wayFilterDistance which governs how large an area surrounding
|
||||
* the requested tile will be returned.
|
||||
* For most use cases the standard settings should be sufficient.
|
||||
*/
|
||||
public static boolean wayFilterEnabled = true;
|
||||
public static int wayFilterDistance = 20;
|
||||
|
||||
private long mFileSize;
|
||||
private boolean mDebugFile;
|
||||
private RandomAccessFile mInputFile;
|
||||
@@ -187,6 +205,9 @@ public class MapDatabase implements ITileDataSource {
|
||||
|
||||
private final MapFileTileSource mTileSource;
|
||||
|
||||
private int zoomLevelMin = 0;
|
||||
private int zoomLevelMax = Byte.MAX_VALUE;
|
||||
|
||||
public MapDatabase(MapFileTileSource tileSource) throws IOException {
|
||||
mTileSource = tileSource;
|
||||
try {
|
||||
@@ -305,7 +326,9 @@ public class MapDatabase implements ITileDataSource {
|
||||
* @param mapDataSink the callback which handles the extracted map elements.
|
||||
*/
|
||||
private void processBlock(QueryParameters queryParameters,
|
||||
SubFileParameter subFileParameter, ITileDataSink mapDataSink) {
|
||||
SubFileParameter subFileParameter, ITileDataSink mapDataSink,
|
||||
BoundingBox boundingBox, Selector selector,
|
||||
MapReadResult mapReadResult) {
|
||||
|
||||
if (!processBlockSignature()) {
|
||||
return;
|
||||
@@ -339,7 +362,13 @@ public class MapDatabase implements ITileDataSource {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!processPOIs(mapDataSink, poisOnQueryZoomLevel)) {
|
||||
boolean filterRequired = queryParameters.queryZoomLevel > subFileParameter.baseZoomLevel;
|
||||
|
||||
List<PointOfInterest> pois = null;
|
||||
if (mapReadResult != null)
|
||||
pois = new ArrayList<>();
|
||||
|
||||
if (!processPOIs(mapDataSink, poisOnQueryZoomLevel, boundingBox, filterRequired, pois)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -355,10 +384,19 @@ public class MapDatabase implements ITileDataSource {
|
||||
/* move the pointer to the first way */
|
||||
mReadBuffer.setBufferPosition(firstWayOffset);
|
||||
|
||||
if (!processWays(queryParameters, mapDataSink, waysOnQueryZoomLevel)) {
|
||||
List<Way> ways = null;
|
||||
if (mapReadResult != null && Selector.POIS != selector)
|
||||
ways = new ArrayList<>();
|
||||
|
||||
if (!processWays(queryParameters, mapDataSink, waysOnQueryZoomLevel, boundingBox, filterRequired, selector, ways)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapReadResult != null) {
|
||||
if (Selector.POIS == selector)
|
||||
ways = Collections.emptyList();
|
||||
mapReadResult.add(new PoiWayBundle(pois, ways));
|
||||
}
|
||||
}
|
||||
|
||||
// private long mCurrentRow;
|
||||
@@ -405,8 +443,26 @@ public class MapDatabase implements ITileDataSource {
|
||||
|
||||
//private final static Tag mWaterTag = new Tag("natural", "water");
|
||||
|
||||
/**
|
||||
* Map rendering.
|
||||
*/
|
||||
private void processBlocks(ITileDataSink mapDataSink, QueryParameters queryParams,
|
||||
SubFileParameter subFileParameter) throws IOException {
|
||||
processBlocks(mapDataSink, queryParams, subFileParameter, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map data reading.
|
||||
*/
|
||||
private void processBlocks(QueryParameters queryParams,
|
||||
SubFileParameter subFileParameter, BoundingBox boundingBox,
|
||||
Selector selector, MapReadResult mapReadResult) throws IOException {
|
||||
processBlocks(null, queryParams, subFileParameter, boundingBox, selector, mapReadResult);
|
||||
}
|
||||
|
||||
private void processBlocks(ITileDataSink mapDataSink, QueryParameters queryParams,
|
||||
SubFileParameter subFileParameter, BoundingBox boundingBox,
|
||||
Selector selector, MapReadResult mapReadResult) throws IOException {
|
||||
|
||||
/* read and process all blocks from top to bottom and from left to right */
|
||||
for (long row = queryParams.fromBlockY; row <= queryParams.toBlockY; row++) {
|
||||
@@ -508,7 +564,7 @@ public class MapDatabase implements ITileDataSource {
|
||||
mTileLatitude = (int) (tileLatitudeDeg * 1E6);
|
||||
mTileLongitude = (int) (tileLongitudeDeg * 1E6);
|
||||
|
||||
processBlock(queryParams, subFileParameter, mapDataSink);
|
||||
processBlock(queryParams, subFileParameter, mapDataSink, boundingBox, selector, mapReadResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -539,7 +595,8 @@ public class MapDatabase implements ITileDataSource {
|
||||
* @return true if the POIs could be processed successfully, false
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean processPOIs(ITileDataSink mapDataSink, int numberOfPois) {
|
||||
private boolean processPOIs(ITileDataSink mapDataSink, int numberOfPois, BoundingBox boundingBox,
|
||||
boolean filterRequired, List<PointOfInterest> pois) {
|
||||
Tag[] poiTags = mTileSource.fileInfo.poiTags;
|
||||
MapElement e = mElem;
|
||||
|
||||
@@ -605,13 +662,26 @@ public class MapDatabase implements ITileDataSource {
|
||||
|
||||
e.setLayer(layer);
|
||||
|
||||
mapDataSink.process(e);
|
||||
if (pois != null) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
for (int i = 0; i < e.tags.numTags; i++)
|
||||
tags.add(e.tags.tags[i]);
|
||||
GeoPoint position = new GeoPoint(latitude, longitude);
|
||||
// depending on the zoom level configuration the poi can lie outside
|
||||
// the tile requested, we filter them out here
|
||||
if (!filterRequired || boundingBox.contains(position)) {
|
||||
pois.add(new PointOfInterest(layer, tags, position));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapDataSink != null)
|
||||
mapDataSink.process(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean processWayDataBlock(MapElement e, boolean doubleDeltaEncoding, boolean isLine) {
|
||||
private boolean processWayDataBlock(MapElement e, boolean doubleDeltaEncoding, boolean isLine, List<GeoPoint[]> wayCoordinates) {
|
||||
/* get and check the number of way coordinate blocks (VBE-U) */
|
||||
int numBlocks = mReadBuffer.readUnsignedInt();
|
||||
if (numBlocks < 1 || numBlocks > Short.MAX_VALUE) {
|
||||
@@ -638,6 +708,14 @@ public class MapDatabase implements ITileDataSource {
|
||||
|
||||
wayLengths[coordinateBlock] = decodeWayNodes(doubleDeltaEncoding,
|
||||
e, len, isLine);
|
||||
|
||||
if (wayCoordinates != null) {
|
||||
// create the array which will store the current way segment
|
||||
GeoPoint[] waySegment = new GeoPoint[e.getNumPoints()];
|
||||
for (int i = 0; i < e.getNumPoints(); i++)
|
||||
waySegment[i] = new GeoPoint(e.getPointY(i) / 1E6, e.getPointX(i) / 1E6);
|
||||
wayCoordinates.add(waySegment);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -712,8 +790,9 @@ public class MapDatabase implements ITileDataSource {
|
||||
* @return true if the ways could be processed successfully, false
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean processWays(QueryParameters queryParameters,
|
||||
ITileDataSink mapDataSink, int numberOfWays) {
|
||||
private boolean processWays(QueryParameters queryParameters, ITileDataSink mapDataSink,
|
||||
int numberOfWays, BoundingBox boundingBox, boolean filterRequired,
|
||||
Selector selector, List<Way> ways) {
|
||||
|
||||
Tag[] wayTags = mTileSource.fileInfo.wayTags;
|
||||
MapElement e = mElem;
|
||||
@@ -866,7 +945,11 @@ public class MapDatabase implements ITileDataSource {
|
||||
for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; wayDataBlock++) {
|
||||
e.clear();
|
||||
|
||||
if (!processWayDataBlock(e, featureWayDoubleDeltaEncoding, linearFeature))
|
||||
List<GeoPoint[]> wayNodes = null;
|
||||
if (ways != null)
|
||||
wayNodes = new ArrayList<>();
|
||||
|
||||
if (!processWayDataBlock(e, featureWayDoubleDeltaEncoding, linearFeature, wayNodes))
|
||||
return false;
|
||||
|
||||
/* drop invalid outer ring */
|
||||
@@ -889,13 +972,112 @@ public class MapDatabase implements ITileDataSource {
|
||||
|
||||
e.setLayer(layer);
|
||||
|
||||
mapDataSink.process(e);
|
||||
if (ways != null) {
|
||||
BoundingBox wayFilterBbox = boundingBox.extendMeters(wayFilterDistance);
|
||||
GeoPoint[][] wayNodesArray = wayNodes.toArray(new GeoPoint[wayNodes.size()][]);
|
||||
if (!filterRequired || !wayFilterEnabled || wayFilterBbox.intersectsArea(wayNodesArray)) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
for (int i = 0; i < e.tags.numTags; i++)
|
||||
tags.add(e.tags.tags[i]);
|
||||
if (Selector.ALL == selector || hasName || hasHouseNr || hasRef || wayAsLabelTagFilter(tags)) {
|
||||
GeoPoint labelPos = e.labelPosition != null ? new GeoPoint(e.labelPosition.y / 1E6, e.labelPosition.x / 1E6) : null;
|
||||
ways.add(new Way(layer, tags, wayNodesArray, labelPos, e.type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mapDataSink != null)
|
||||
mapDataSink.process(e);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads only labels for tile.
|
||||
*
|
||||
* @param tile tile for which data is requested.
|
||||
* @return label data for the tile.
|
||||
*/
|
||||
public MapReadResult readLabels(Tile tile) {
|
||||
return readMapData(tile, tile, Selector.LABELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data for an area defined by the tile in the upper left and the tile in
|
||||
* the lower right corner.
|
||||
* Precondition: upperLeft.tileX <= lowerRight.tileX && upperLeft.tileY <= lowerRight.tileY
|
||||
*
|
||||
* @param upperLeft tile that defines the upper left corner of the requested area.
|
||||
* @param lowerRight tile that defines the lower right corner of the requested area.
|
||||
* @return map data for the tile.
|
||||
*/
|
||||
public MapReadResult readLabels(Tile upperLeft, Tile lowerRight) {
|
||||
return readMapData(upperLeft, lowerRight, Selector.LABELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all map data for the area covered by the given tile at the tile zoom level.
|
||||
*
|
||||
* @param tile defines area and zoom level of read map data.
|
||||
* @return the read map data.
|
||||
*/
|
||||
public MapReadResult readMapData(Tile tile) {
|
||||
return readMapData(tile, tile, Selector.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data for an area defined by the tile in the upper left and the tile in
|
||||
* the lower right corner.
|
||||
* Precondition: upperLeft.tileX <= lowerRight.tileX && upperLeft.tileY <= lowerRight.tileY
|
||||
*
|
||||
* @param upperLeft tile that defines the upper left corner of the requested area.
|
||||
* @param lowerRight tile that defines the lower right corner of the requested area.
|
||||
* @return map data for the tile.
|
||||
*/
|
||||
public MapReadResult readMapData(Tile upperLeft, Tile lowerRight) {
|
||||
return readMapData(upperLeft, lowerRight, Selector.ALL);
|
||||
}
|
||||
|
||||
private MapReadResult readMapData(Tile upperLeft, Tile lowerRight, Selector selector) {
|
||||
if (mTileSource.fileHeader == null)
|
||||
return null;
|
||||
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
|
||||
if (mIntBuffer == null)
|
||||
mIntBuffer = new int[Short.MAX_VALUE * 2];
|
||||
|
||||
try {
|
||||
mTileProjection.setTile(upperLeft);
|
||||
|
||||
QueryParameters queryParameters = new QueryParameters();
|
||||
queryParameters.queryZoomLevel =
|
||||
mTileSource.fileHeader.getQueryZoomLevel(upperLeft.zoomLevel);
|
||||
|
||||
/* get and check the sub-file for the query zoom level */
|
||||
SubFileParameter subFileParameter =
|
||||
mTileSource.fileHeader.getSubFileParameter(queryParameters.queryZoomLevel);
|
||||
|
||||
if (subFileParameter == null) {
|
||||
log.warn("no sub-file for zoom level: "
|
||||
+ queryParameters.queryZoomLevel);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
QueryCalculations.calculateBaseTiles(queryParameters, upperLeft, lowerRight, subFileParameter);
|
||||
QueryCalculations.calculateBlocks(queryParameters, subFileParameter);
|
||||
processBlocks(queryParameters, subFileParameter, Tile.getBoundingBox(upperLeft, lowerRight), selector, mapReadResult);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
private int[] readOptionalLabelPosition() {
|
||||
int[] labelPosition = new int[2];
|
||||
|
||||
@@ -908,6 +1090,29 @@ public class MapDatabase implements ITileDataSource {
|
||||
return labelPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads only POI data for tile.
|
||||
*
|
||||
* @param tile tile for which data is requested.
|
||||
* @return POI data for the tile.
|
||||
*/
|
||||
public MapReadResult readPoiData(Tile tile) {
|
||||
return readMapData(tile, tile, Selector.POIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads POI data for an area defined by the tile in the upper left and the tile in
|
||||
* the lower right corner.
|
||||
* This implementation takes the data storage of a MapFile into account for greater efficiency.
|
||||
*
|
||||
* @param upperLeft tile that defines the upper left corner of the requested area.
|
||||
* @param lowerRight tile that defines the lower right corner of the requested area.
|
||||
* @return map data for the tile.
|
||||
*/
|
||||
public MapReadResult readPoiData(Tile upperLeft, Tile lowerRight) {
|
||||
return readMapData(upperLeft, lowerRight, Selector.POIS);
|
||||
}
|
||||
|
||||
private int[][] readZoomTable(SubFileParameter subFileParameter) {
|
||||
int rows = subFileParameter.zoomLevelMax - subFileParameter.zoomLevelMin + 1;
|
||||
int[][] zoomTable = new int[rows][2];
|
||||
@@ -926,6 +1131,51 @@ public class MapDatabase implements ITileDataSource {
|
||||
return zoomTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts returns of data to zoom level range specified. This can be used to restrict
|
||||
* the use of this map data base when used in MultiMapDatabase settings.
|
||||
*
|
||||
* @param minZoom minimum zoom level supported
|
||||
* @param maxZoom maximum zoom level supported
|
||||
*/
|
||||
public void restrictToZoomRange(int minZoom, int maxZoom) {
|
||||
this.zoomLevelMax = maxZoom;
|
||||
this.zoomLevelMin = minZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if MapDatabase contains tile.
|
||||
*
|
||||
* @param tile tile to be rendered.
|
||||
* @return true if tile is part of database.
|
||||
*/
|
||||
public boolean supportsTile(Tile tile) {
|
||||
return tile.getBoundingBox().intersects(mTileSource.getMapInfo().boundingBox)
|
||||
&& (tile.zoomLevel >= this.zoomLevelMin && tile.zoomLevel <= this.zoomLevelMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a way should be included in the result set for readLabels()
|
||||
* By default only ways with names, house numbers or a ref are included in the result set
|
||||
* of readLabels(). This is to reduce the set of ways as much as possible to save memory.
|
||||
*
|
||||
* @param tags the tags associated with the way
|
||||
* @return true if the way should be included in the result set
|
||||
*/
|
||||
public boolean wayAsLabelTagFilter(List<Tag> tags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Selector enum is used to specify which data subset is to be retrieved from a MapFile:
|
||||
* ALL: all data (as in version 0.6.0)
|
||||
* POIS: only poi data, no ways (new after 0.6.0)
|
||||
* LABELS: poi data and ways that have a name (new after 0.6.0)
|
||||
*/
|
||||
private enum Selector {
|
||||
ALL, POIS, LABELS
|
||||
}
|
||||
|
||||
static class TileProjection {
|
||||
private static final double COORD_SCALE = 1000000.0;
|
||||
|
||||
@@ -947,7 +1197,7 @@ public class MapDatabase implements ITileDataSource {
|
||||
/* scales longitude(1e6) to map-pixel */
|
||||
divx = (180.0 * COORD_SCALE) / (mapExtents >> 1);
|
||||
|
||||
/* scale latidute to map-pixel */
|
||||
/* scale latitude to map-pixel */
|
||||
divy = (Math.PI * 2.0) / (mapExtents >> 1);
|
||||
}
|
||||
|
||||
|
||||
77
vtm/src/org/oscim/tiling/source/mapfile/MapReadResult.java
Normal file
77
vtm/src/org/oscim/tiling/source/mapfile/MapReadResult.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012, 2013 mapsforge.org
|
||||
* Copyright 2014-2015 Ludwig M Brinckmann
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* 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.mapfile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable container for the data returned from a MapDataStore.
|
||||
*/
|
||||
public class MapReadResult {
|
||||
|
||||
/**
|
||||
* True if the read area is completely covered by water, false otherwise.
|
||||
*/
|
||||
public boolean isWater;
|
||||
|
||||
/**
|
||||
* The read POIs.
|
||||
*/
|
||||
public List<PointOfInterest> pointOfInterests;
|
||||
|
||||
/**
|
||||
* The read ways.
|
||||
*/
|
||||
public List<Way> ways;
|
||||
|
||||
public MapReadResult() {
|
||||
this.pointOfInterests = new ArrayList<>();
|
||||
this.ways = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void add(PoiWayBundle poiWayBundle) {
|
||||
this.pointOfInterests.addAll(poiWayBundle.pois);
|
||||
this.ways.addAll(poiWayBundle.ways);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds other MapReadResult by combining pois and ways. Optionally, deduplication can
|
||||
* be requested (much more expensive).
|
||||
*
|
||||
* @param other the MapReadResult to add to this.
|
||||
* @param deduplicate true if check for duplicates is required.
|
||||
*/
|
||||
public void add(MapReadResult other, boolean deduplicate) {
|
||||
if (deduplicate) {
|
||||
for (PointOfInterest poi : other.pointOfInterests) {
|
||||
if (!this.pointOfInterests.contains(poi)) {
|
||||
this.pointOfInterests.add(poi);
|
||||
}
|
||||
}
|
||||
for (Way way : other.ways) {
|
||||
if (!this.ways.contains(way)) {
|
||||
this.ways.add(way);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.pointOfInterests.addAll(other.pointOfInterests);
|
||||
this.ways.addAll(other.ways);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016-2017 devemux86
|
||||
*
|
||||
* 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
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.oscim.tiling.source.mapfile;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.tiling.ITileDataSink;
|
||||
import org.oscim.tiling.ITileDataSource;
|
||||
@@ -41,8 +42,7 @@ public class MultiMapDatabase implements ITileDataSource {
|
||||
public void query(MapTile tile, ITileDataSink mapDataSink) {
|
||||
MultiMapDataSink multiMapDataSink = new MultiMapDataSink(mapDataSink);
|
||||
for (MapDatabase mapDatabase : mapDatabases) {
|
||||
int[] zoomLevels = tileSource.getZoomsByTileSource().get(mapDatabase.getTileSource());
|
||||
if (zoomLevels == null || (zoomLevels[0] <= tile.zoomLevel && tile.zoomLevel <= zoomLevels[1]))
|
||||
if (mapDatabase.supportsTile(tile))
|
||||
mapDatabase.query(tile, multiMapDataSink);
|
||||
}
|
||||
mapDataSink.completed(multiMapDataSink.getResult());
|
||||
@@ -61,4 +61,109 @@ public class MultiMapDatabase implements ITileDataSource {
|
||||
mapDatabase.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public MapReadResult readLabels(Tile tile) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(tile)) {
|
||||
MapReadResult result = mdb.readLabels(tile);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public MapReadResult readLabels(Tile upperLeft, Tile lowerRight) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(upperLeft)) {
|
||||
MapReadResult result = mdb.readLabels(upperLeft, lowerRight);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public MapReadResult readMapData(Tile tile) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(tile)) {
|
||||
MapReadResult result = mdb.readMapData(tile);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public MapReadResult readMapData(Tile upperLeft, Tile lowerRight) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(upperLeft)) {
|
||||
MapReadResult result = mdb.readMapData(upperLeft, lowerRight);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public MapReadResult readPoiData(Tile tile) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(tile)) {
|
||||
MapReadResult result = mdb.readPoiData(tile);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public MapReadResult readPoiData(Tile upperLeft, Tile lowerRight) {
|
||||
MapReadResult mapReadResult = new MapReadResult();
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(upperLeft)) {
|
||||
MapReadResult result = mdb.readPoiData(upperLeft, lowerRight);
|
||||
if (result == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isWater = mapReadResult.isWater & result.isWater;
|
||||
mapReadResult.isWater = isWater;
|
||||
mapReadResult.add(result, false);
|
||||
}
|
||||
}
|
||||
return mapReadResult;
|
||||
}
|
||||
|
||||
public boolean supportsTile(Tile tile) {
|
||||
for (MapDatabase mdb : mapDatabases) {
|
||||
if (mdb.supportsTile(tile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 devemux86
|
||||
* Copyright 2016-2017 devemux86
|
||||
*
|
||||
* 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
|
||||
@@ -63,16 +63,16 @@ public class MultiMapFileTileSource extends TileSource implements IMapFileTileSo
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
Map<MapFileTileSource, int[]> getZoomsByTileSource() {
|
||||
return zoomsByTileSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
MultiMapDatabase multiMapDatabase = new MultiMapDatabase(this);
|
||||
for (MapFileTileSource mapFileTileSource : mapFileTileSources) {
|
||||
try {
|
||||
multiMapDatabase.add(new MapDatabase(mapFileTileSource));
|
||||
MapDatabase mapDatabase = new MapDatabase(mapFileTileSource);
|
||||
int[] zoomLevels = zoomsByTileSource.get(mapFileTileSource);
|
||||
if (zoomLevels != null)
|
||||
mapDatabase.restrictToZoomRange(zoomLevels[0], zoomLevels[1]);
|
||||
multiMapDatabase.add(mapDatabase);
|
||||
} catch (IOException e) {
|
||||
log.debug(e.getMessage());
|
||||
}
|
||||
|
||||
28
vtm/src/org/oscim/tiling/source/mapfile/PoiWayBundle.java
Normal file
28
vtm/src/org/oscim/tiling/source/mapfile/PoiWayBundle.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012, 2013 mapsforge.org
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* 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.mapfile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PoiWayBundle {
|
||||
final List<PointOfInterest> pois;
|
||||
final List<Way> ways;
|
||||
|
||||
public PoiWayBundle(List<PointOfInterest> pois, List<Way> ways) {
|
||||
this.pois = pois;
|
||||
this.ways = ways;
|
||||
}
|
||||
}
|
||||
77
vtm/src/org/oscim/tiling/source/mapfile/PointOfInterest.java
Normal file
77
vtm/src/org/oscim/tiling/source/mapfile/PointOfInterest.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012, 2013 mapsforge.org
|
||||
* Copyright 2014-2015 Ludwig M Brinckmann
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* 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.mapfile;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable container for all data associated with a single point of interest node (POI).
|
||||
*/
|
||||
public class PointOfInterest {
|
||||
/**
|
||||
* The layer of this POI + 5 (to avoid negative values).
|
||||
*/
|
||||
public final byte layer;
|
||||
|
||||
/**
|
||||
* The position of this POI.
|
||||
*/
|
||||
public final GeoPoint position;
|
||||
|
||||
/**
|
||||
* The tags of this POI.
|
||||
*/
|
||||
public final List<Tag> tags;
|
||||
|
||||
public PointOfInterest(byte layer, List<Tag> tags, GeoPoint position) {
|
||||
this.layer = layer;
|
||||
this.tags = tags;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (!(obj instanceof PointOfInterest)) {
|
||||
return false;
|
||||
}
|
||||
PointOfInterest other = (PointOfInterest) obj;
|
||||
if (this.layer != other.layer) {
|
||||
return false;
|
||||
} else if (!this.tags.equals(other.tags)) {
|
||||
return false;
|
||||
} else if (!this.position.equals(other.position)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + layer;
|
||||
result = prime * result + tags.hashCode();
|
||||
result = prime * result + position.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2014-2015 Ludwig M Brinckmann
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@@ -100,18 +102,14 @@ final class QueryCalculations {
|
||||
}
|
||||
}
|
||||
|
||||
static void calculateBaseTiles(QueryParameters queryParameters, Tile tile,
|
||||
SubFileParameter subFileParameter) {
|
||||
static void calculateBaseTiles(QueryParameters queryParameters, Tile tile, SubFileParameter subFileParameter) {
|
||||
if (tile.zoomLevel < subFileParameter.baseZoomLevel) {
|
||||
// calculate the XY numbers of the upper left and lower right
|
||||
// sub-tiles
|
||||
// calculate the XY numbers of the upper left and lower right sub-tiles
|
||||
int zoomLevelDifference = subFileParameter.baseZoomLevel - tile.zoomLevel;
|
||||
queryParameters.fromBaseTileX = tile.tileX << zoomLevelDifference;
|
||||
queryParameters.fromBaseTileY = tile.tileY << zoomLevelDifference;
|
||||
queryParameters.toBaseTileX = queryParameters.fromBaseTileX
|
||||
+ (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.toBaseTileY = queryParameters.fromBaseTileY
|
||||
+ (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.toBaseTileX = queryParameters.fromBaseTileX + (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.toBaseTileY = queryParameters.fromBaseTileY + (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.useTileBitmask = false;
|
||||
} else if (tile.zoomLevel > subFileParameter.baseZoomLevel) {
|
||||
// calculate the XY numbers of the parent base tile
|
||||
@@ -132,6 +130,37 @@ final class QueryCalculations {
|
||||
}
|
||||
}
|
||||
|
||||
static void calculateBaseTiles(QueryParameters queryParameters, Tile upperLeft, Tile lowerRight, SubFileParameter subFileParameter) {
|
||||
if (upperLeft.zoomLevel < subFileParameter.baseZoomLevel) {
|
||||
// here we need to combine multiple base tiles
|
||||
int zoomLevelDifference = subFileParameter.baseZoomLevel - upperLeft.zoomLevel;
|
||||
queryParameters.fromBaseTileX = upperLeft.tileX << zoomLevelDifference;
|
||||
queryParameters.fromBaseTileY = upperLeft.tileY << zoomLevelDifference;
|
||||
queryParameters.toBaseTileX = (lowerRight.tileX << zoomLevelDifference) + (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.toBaseTileY = (lowerRight.tileY << zoomLevelDifference) + (1 << zoomLevelDifference) - 1;
|
||||
queryParameters.useTileBitmask = false;
|
||||
} else if (upperLeft.zoomLevel > subFileParameter.baseZoomLevel) {
|
||||
// we might have more than just one base tile as we might span boundaries
|
||||
int zoomLevelDifference = upperLeft.zoomLevel - subFileParameter.baseZoomLevel;
|
||||
queryParameters.fromBaseTileX = upperLeft.tileX >>> zoomLevelDifference;
|
||||
queryParameters.fromBaseTileY = upperLeft.tileY >>> zoomLevelDifference;
|
||||
queryParameters.toBaseTileX = lowerRight.tileX >>> zoomLevelDifference;
|
||||
queryParameters.toBaseTileY = lowerRight.tileY >>> zoomLevelDifference;
|
||||
// TODO understand what is going on here. The tileBitmask is used to extract just
|
||||
// the data from the base tiles that is relevant for the area, but how can this work
|
||||
// for a set of tiles, so not using tileBitmask for the moment.
|
||||
queryParameters.useTileBitmask = true;
|
||||
queryParameters.queryTileBitmask = QueryCalculations.calculateTileBitmask(upperLeft, lowerRight, zoomLevelDifference);
|
||||
} else {
|
||||
// we are on the base zoom level, so we just need all tiles in range
|
||||
queryParameters.fromBaseTileX = upperLeft.tileX;
|
||||
queryParameters.fromBaseTileY = upperLeft.tileY;
|
||||
queryParameters.toBaseTileX = lowerRight.tileX;
|
||||
queryParameters.toBaseTileY = lowerRight.tileY;
|
||||
queryParameters.useTileBitmask = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void calculateBlocks(QueryParameters queryParameters, SubFileParameter subFileParameter) {
|
||||
// calculate the blocks in the file which need to be read
|
||||
queryParameters.fromBlockX = Math.max(queryParameters.fromBaseTileX
|
||||
@@ -171,6 +200,17 @@ final class QueryCalculations {
|
||||
}
|
||||
}
|
||||
|
||||
static int calculateTileBitmask(Tile upperLeft, Tile lowerRight, int zoomLevelDifference) {
|
||||
int bitmask = 0;
|
||||
for (int x = upperLeft.tileX; x <= lowerRight.tileX; x++) {
|
||||
for (int y = upperLeft.tileY; y <= lowerRight.tileY; y++) {
|
||||
Tile current = new Tile(x, y, upperLeft.zoomLevel);
|
||||
bitmask |= calculateTileBitmask(current, zoomLevelDifference);
|
||||
}
|
||||
}
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
private QueryCalculations() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
114
vtm/src/org/oscim/tiling/source/mapfile/Way.java
Normal file
114
vtm/src/org/oscim/tiling/source/mapfile/Way.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012, 2013 mapsforge.org
|
||||
* Copyright 2014-2015 Ludwig M Brinckmann
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* 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.mapfile;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.Tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable container for all data associated with a single way or area (closed way).
|
||||
*/
|
||||
public class Way {
|
||||
/**
|
||||
* The position of the area label (may be null).
|
||||
*/
|
||||
public final GeoPoint labelPosition;
|
||||
|
||||
/**
|
||||
* The geometry type.
|
||||
*/
|
||||
public GeometryBuffer.GeometryType geometryType = GeometryBuffer.GeometryType.NONE;
|
||||
|
||||
/**
|
||||
* The geographical coordinates of the way nodes.
|
||||
*/
|
||||
public final GeoPoint[][] geoPoints;
|
||||
|
||||
/**
|
||||
* The layer of this way + 5 (to avoid negative values).
|
||||
*/
|
||||
public final byte layer;
|
||||
|
||||
/**
|
||||
* The tags of this way.
|
||||
*/
|
||||
public final List<Tag> tags;
|
||||
|
||||
public Way(byte layer, List<Tag> tags, GeoPoint[][] geoPoints, GeoPoint labelPosition) {
|
||||
this.layer = layer;
|
||||
this.tags = tags;
|
||||
this.geoPoints = geoPoints;
|
||||
this.labelPosition = labelPosition;
|
||||
}
|
||||
|
||||
public Way(byte layer, List<Tag> tags, GeoPoint[][] geoPoints, GeoPoint labelPosition, final GeometryBuffer.GeometryType geometryType) {
|
||||
this(layer, tags, geoPoints, labelPosition);
|
||||
this.geometryType = geometryType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (!(obj instanceof Way)) {
|
||||
return false;
|
||||
}
|
||||
Way other = (Way) obj;
|
||||
if (this.layer != other.layer) {
|
||||
return false;
|
||||
} else if (!this.tags.equals(other.tags)) {
|
||||
return false;
|
||||
} else if (this.labelPosition == null && other.labelPosition != null) {
|
||||
return false;
|
||||
} else if (this.labelPosition != null && !this.labelPosition.equals(other.labelPosition)) {
|
||||
return false;
|
||||
} else if (this.geoPoints.length != other.geoPoints.length) {
|
||||
return false;
|
||||
} else {
|
||||
for (int i = 0; i < this.geoPoints.length; i++) {
|
||||
if (this.geoPoints[i].length != other.geoPoints[i].length) {
|
||||
return false;
|
||||
} else {
|
||||
for (int j = 0; j < this.geoPoints[i].length; j++) {
|
||||
if (!geoPoints[i][j].equals(other.geoPoints[i][j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + layer;
|
||||
result = prime * result + tags.hashCode();
|
||||
result = prime * result + Arrays.deepHashCode(geoPoints);
|
||||
if (labelPosition != null) {
|
||||
result = prime * result + labelPosition.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user