Mapsforge Reverse Geocoding #383 (#398)

* Mapsforge Reverse Geocoding #383

* Mapsforge Reverse Geocoding example #383
This commit is contained in:
Emux
2017-09-10 17:04:19 +03:00
committed by GitHub
parent 095ed65eb3
commit 5c2b404b3f
16 changed files with 887 additions and 36 deletions

View File

@@ -6,6 +6,7 @@
- Render themes: line symbol [#124](https://github.com/mapsforge/vtm/issues/124)
- Render themes: stroke dash array [#131](https://github.com/mapsforge/vtm/issues/131)
- POI Search example [#394](https://github.com/mapsforge/vtm/issues/394)
- Mapsforge Reverse Geocoding [#383](https://github.com/mapsforge/vtm/issues/383)
- Core utilities [#396](https://github.com/mapsforge/vtm/issues/396)
- Mapsforge fix artifacts zoom > 17 [#231](https://github.com/mapsforge/vtm/issues/231)
- vtm-theme-comparator module [#387](https://github.com/mapsforge/vtm/issues/387)

View File

@@ -97,6 +97,9 @@
<activity
android:name=".POTTextureActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".ReverseGeocodeActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".RotateMarkerOverlayActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />

View File

@@ -18,5 +18,6 @@
<string name="style_1">Show nature</string>
<string name="style_2">Hide nature</string>
<string name="menu_gridlayer">Grid</string>
<string name="dialog_reverse_geocoding_title">Reverse Geocoding</string>
</resources>

View File

@@ -53,6 +53,7 @@ public class MapsforgeMapActivity extends MapActivity {
private TileGridLayer mGridLayer;
private DefaultMapScaleBar mMapScaleBar;
private Menu mMenu;
MapFileTileSource mTileSource;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -152,12 +153,12 @@ public class MapsforgeMapActivity extends MapActivity {
return;
}
MapFileTileSource tileSource = new MapFileTileSource();
tileSource.setPreferredLanguage("en");
mTileSource = new MapFileTileSource();
mTileSource.setPreferredLanguage("en");
String file = intent.getStringExtra(FilePicker.SELECTED_FILE);
if (tileSource.setMapFile(file)) {
if (mTileSource.setMapFile(file)) {
VectorTileLayer l = mMap.setBaseMap(tileSource);
VectorTileLayer l = mMap.setBaseMap(mTileSource);
loadTheme(null);
mMap.layers().add(new BuildingLayer(mMap, l));
@@ -175,7 +176,7 @@ public class MapsforgeMapActivity extends MapActivity {
renderer.setOffset(5 * getResources().getDisplayMetrics().density, 0);
mMap.layers().add(mapScaleBarLayer);
MapInfo info = tileSource.getMapInfo();
MapInfo info = mTileSource.getMapInfo();
MapPosition pos = new MapPosition();
pos.setByBoundingBox(info.boundingBox, Tile.SIZE * 4, Tile.SIZE * 4);
mMap.setMapPosition(pos);

View File

@@ -86,8 +86,11 @@ public class PoiSearchActivity extends MapsforgeMapActivity implements ItemizedL
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == SELECT_MAP_FILE) {
startActivityForResult(new Intent(this, PoiFilePicker.class),
SELECT_POI_FILE);
if (mTileSource != null)
startActivityForResult(new Intent(this, PoiFilePicker.class),
SELECT_POI_FILE);
else
finish();
} else if (requestCode == SELECT_POI_FILE) {
if (resultCode != RESULT_OK || intent == null || intent.getStringExtra(FilePicker.SELECTED_FILE) == null) {
finish();

View File

@@ -0,0 +1,145 @@
/*
* 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.android.test;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import org.oscim.backend.CanvasAdapter;
import org.oscim.core.GeoPoint;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Point;
import org.oscim.core.Tag;
import org.oscim.core.Tile;
import org.oscim.event.Gesture;
import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
import org.oscim.layers.Layer;
import org.oscim.layers.TileGridLayer;
import org.oscim.map.Map;
import org.oscim.tiling.source.mapfile.MapDatabase;
import org.oscim.tiling.source.mapfile.MapReadResult;
import org.oscim.tiling.source.mapfile.PointOfInterest;
import org.oscim.tiling.source.mapfile.Way;
import org.oscim.utils.GeoPointUtils;
import java.util.List;
/**
* Reverse Geocoding with long press.
* <p/>
* - POI in specified radius.<br/>
* - Ways containing touch point.
*/
public class ReverseGeocodeActivity extends MapsforgeMapActivity {
private static final int TOUCH_RADIUS = 32 / 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Map events receiver
mMap.layers().add(new MapEventsReceiver(mMap));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == SELECT_MAP_FILE) {
// For debug
TileGridLayer gridLayer = new TileGridLayer(mMap, getResources().getDisplayMetrics().density);
mMap.layers().add(gridLayer);
}
}
private class MapEventsReceiver extends Layer implements GestureListener {
MapEventsReceiver(Map map) {
super(map);
}
@Override
public boolean onGesture(Gesture g, MotionEvent e) {
if (g instanceof Gesture.LongPress) {
GeoPoint p = mMap.viewport().fromScreenPoint(e.getX(), e.getY());
// Read all labeled POI and ways for the area covered by the tiles under touch
float touchRadius = TOUCH_RADIUS * CanvasAdapter.dpi / CanvasAdapter.DEFAULT_DPI;
long mapSize = MercatorProjection.getMapSize((byte) mMap.getMapPosition().getZoomLevel());
double pixelX = MercatorProjection.longitudeToPixelX(p.getLongitude(), mapSize);
double pixelY = MercatorProjection.latitudeToPixelY(p.getLatitude(), mapSize);
int tileXMin = MercatorProjection.pixelXToTileX(pixelX - touchRadius, (byte) mMap.getMapPosition().getZoomLevel());
int tileXMax = MercatorProjection.pixelXToTileX(pixelX + touchRadius, (byte) mMap.getMapPosition().getZoomLevel());
int tileYMin = MercatorProjection.pixelYToTileY(pixelY - touchRadius, (byte) mMap.getMapPosition().getZoomLevel());
int tileYMax = MercatorProjection.pixelYToTileY(pixelY + touchRadius, (byte) mMap.getMapPosition().getZoomLevel());
Tile upperLeft = new Tile(tileXMin, tileYMin, (byte) mMap.getMapPosition().getZoomLevel());
Tile lowerRight = new Tile(tileXMax, tileYMax, (byte) mMap.getMapPosition().getZoomLevel());
MapReadResult mapReadResult = ((MapDatabase) mTileSource.getDataSource()).readLabels(upperLeft, lowerRight);
StringBuilder sb = new StringBuilder();
// Filter POI
sb.append("*** POI ***");
for (PointOfInterest pointOfInterest : mapReadResult.pointOfInterests) {
Point layerXY = new Point();
mMap.viewport().toScreenPoint(pointOfInterest.position, false, layerXY);
Point tapXY = new Point(e.getX(), e.getY());
if (layerXY.distance(tapXY) > touchRadius) {
continue;
}
sb.append("\n");
List<Tag> tags = pointOfInterest.tags;
for (Tag tag : tags) {
sb.append("\n").append(tag.key).append("=").append(tag.value);
}
}
// Filter ways
sb.append("\n\n").append("*** WAYS ***");
for (Way way : mapReadResult.ways) {
if (way.geometryType != GeometryBuffer.GeometryType.POLY
|| !GeoPointUtils.contains(way.geoPoints[0], p)) {
continue;
}
sb.append("\n");
List<Tag> tags = way.tags;
for (Tag tag : tags) {
sb.append("\n").append(tag.key).append("=").append(tag.value);
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(ReverseGeocodeActivity.this);
builder.setIcon(android.R.drawable.ic_menu_search);
builder.setTitle(R.string.dialog_reverse_geocoding_title);
builder.setMessage(sb);
builder.setPositiveButton(R.string.ok, null);
builder.show();
return true;
}
return false;
}
}
}

View File

@@ -122,6 +122,7 @@ public class Samples extends Activity {
linearLayout.addView(createButton(MultiMapActivity.class));
linearLayout.addView(createLabel("Experiments"));
linearLayout.addView(createButton(ReverseGeocodeActivity.class));
linearLayout.addView(createButton(MapPositionActivity.class));
linearLayout.addView(createButton(S3DBMapActivity.class));
linearLayout.addView(createButton(ThemeStylerActivity.class));

View File

@@ -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);

View File

@@ -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);
}

View 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}

View 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;
}
}

View 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;
}
}

View File

@@ -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();
}

View 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;
}
}