org.mapsforge.map.writer.BaseTileBasedDataProcessor Maven / Gradle / Ivy
Show all versions of mapsforge-map-writer Show documentation
/*
* Copyright 2010, 2011, 2012, 2013 mapsforge.org
* Copyright 2015 lincomatic
* Copyright 2017-2018 Gustl22
* Copyright 2017-2019 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 .
*/
package org.mapsforge.map.writer;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.map.hash.TShortIntHashMap;
import gnu.trove.procedure.TObjectProcedure;
import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.TopologyException;
import org.mapsforge.core.model.BoundingBox;
import org.mapsforge.core.util.LatLongUtils;
import org.mapsforge.core.util.MercatorProjection;
import org.mapsforge.map.writer.model.*;
import org.mapsforge.map.writer.util.GeoUtils;
import org.mapsforge.map.writer.util.OSMUtils;
import org.openstreetmap.osmosis.core.domain.v0_6.Node;
import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
import org.openstreetmap.osmosis.core.domain.v0_6.Way;
import java.text.NumberFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
abstract class BaseTileBasedDataProcessor implements TileBasedDataProcessor, NodeResolver, WayResolver {
protected class RelationHandler implements TObjectProcedure {
private List> extractedPolygons;
private List inner;
private Map> outerToInner;
private final WayPolygonizer polygonizer = new WayPolygonizer();
private long nRelations = 0;
@Override
public boolean execute(TDRelation relation) {
if (relation == null) {
return false;
}
if (++this.nRelations % 100 == 0) {
if (progressLogs) {
System.out.print("Progress: Relations " + nfCounts.format(this.nRelations)
+ " / " + nfCounts.format(getRelationsNumber()) + "\r");
}
}
this.extractedPolygons = null;
this.outerToInner = null;
TDWay[] members = relation.getMemberWays();
try {
this.polygonizer.polygonizeAndRelate(members);
} catch (TopologyException e) {
LOGGER.log(Level.FINE,
"cannot relate extracted polygons to each other for relation: " + relation.getId(), e);
}
// skip invalid relations
if (!this.polygonizer.getDangling().isEmpty()) {
if (BaseTileBasedDataProcessor.this.skipInvalidRelations) {
LOGGER.fine("skipping relation that contains dangling ways which could not be merged to polygons: "
+ relation.getId());
return true;
}
LOGGER.fine("relation contains dangling ways which could not be merged to polygons: "
+ relation.getId());
} else if (!this.polygonizer.getIllegal().isEmpty()) {
if (BaseTileBasedDataProcessor.this.skipInvalidRelations) {
LOGGER.fine("skipping relation contains illegal closed ways with fewer than 4 nodes: "
+ relation.getId());
return true;
}
LOGGER.fine("relation contains illegal closed ways with fewer than 4 nodes: " + relation.getId());
}
this.extractedPolygons = this.polygonizer.getPolygons();
this.outerToInner = this.polygonizer.getOuterToInner();
for (Entry> entry : this.outerToInner.entrySet()) {
Deque outerPolygon = this.extractedPolygons.get(entry.getKey().intValue());
this.inner = null;
this.inner = entry.getValue();
byte shape = TDWay.SIMPLE_POLYGON;
// does it contain inner ways?
if (this.inner != null && !this.inner.isEmpty()) {
shape = TDWay.MULTI_POLYGON;
}
TDWay outerWay = null;
if (outerPolygon.size() > 1) {
// we need to create a new way from a set of ways
// collect the way nodes and use the tags of the relation
// if one of the ways has its own tags, we should ignore them,
// ways with relevant tags will be added separately later
if (!relation.isRenderRelevant()) {
LOGGER.fine("constructed outer polygon in relation has no known tags: " + relation.getId());
continue;
}
// merge way nodes from outer way segments
List waynodeList = new ArrayList<>();
for (TDWay outerSegment : outerPolygon) {
if (outerSegment.isReversedInRelation()) {
for (int i = outerSegment.getWayNodes().length - 1; i >= 0; i--) {
waynodeList.add(outerSegment.getWayNodes()[i]);
}
} else {
for (TDNode tdNode : outerSegment.getWayNodes()) {
waynodeList.add(tdNode);
}
}
}
TDNode[] waynodes = waynodeList.toArray(new TDNode[waynodeList.size()]);
// create new virtual way which represents the outer way
// use maxWayID counter to create unique id
outerWay = new TDWay(++BaseTileBasedDataProcessor.this.maxWayID, relation.getLayer(),
relation.getName(), relation.getHouseNumber(), relation.getRef(), relation.getTags(),
shape, waynodes);
// add the newly created way to matching tiles
addWayToTiles(outerWay, BaseTileBasedDataProcessor.this.bboxEnlargement);
handleVirtualOuterWay(outerWay);
// adjust tag statistics, cannot be omitted!!!
countWayTags(relation.getTags().keySet());
}
// the outer way consists of only one segment
else {
outerWay = outerPolygon.getFirst();
// is it a polygon that we have seen already and which was
// identified as a polgyon containing inner ways?
if (BaseTileBasedDataProcessor.this.outerToInnerMapping.contains(outerWay.getId())) {
shape = TDWay.MULTI_POLYGON;
}
outerWay.setShape(shape);
// we merge the name, ref, tag information of the relation to the outer way
// TODO is this true?
// a relation that addresses an already closed way, is normally used to add
// additional information to the way
outerWay.mergeRelationInformation(relation);
// only consider the way, if it has tags, otherwise the renderer cannot interpret
// the way
if (outerWay.isRenderRelevant()) {
// handle relation tags
handleAdditionalRelationTags(outerWay, relation);
addWayToTiles(outerWay, BaseTileBasedDataProcessor.this.bboxEnlargement);
countWayTags(outerWay.getTags().keySet());
}
}
// relate inner ways to outer way
addInnerWays(outerWay);
}
return true;
}
private void addInnerWays(TDWay outer) {
if (this.inner != null && !this.inner.isEmpty()) {
TLongArrayList innerList = BaseTileBasedDataProcessor.this.outerToInnerMapping.get(outer.getId());
if (innerList == null) {
innerList = new TLongArrayList();
BaseTileBasedDataProcessor.this.outerToInnerMapping.put(outer.getId(), innerList);
}
for (Integer innerIndex : this.inner) {
Deque innerSegments = this.extractedPolygons.get(innerIndex.intValue());
TDWay innerWay = null;
if (innerSegments.size() == 1) {
innerWay = innerSegments.getFirst();
if (innerWay.hasTags() && outer.hasTags()) {
int contained = 0;
for (Entry innerTag : innerWay.getTags().entrySet()) {
for (Entry outerTag : outer.getTags().entrySet()) {
if (innerTag.getKey().equals(outerTag.getKey())) {
Object innerValue = innerTag.getValue();
Object outerValue = outerTag.getValue();
if (innerValue != null) {
if (outerValue != null) {
if ((innerValue instanceof Byte && outerValue instanceof Byte && innerValue.equals(outerValue))
|| (innerValue instanceof Integer && outerValue instanceof Integer && innerValue.equals(outerValue))
|| (innerValue instanceof Float && outerValue instanceof Float && innerValue.equals(outerValue))
|| (innerValue instanceof Short && outerValue instanceof Short && innerValue.equals(outerValue))
|| (innerValue instanceof String && outerValue instanceof String && innerValue.equals(outerValue))) {
contained++;
}
}
} else if (outerValue == null) {
contained++;
}
}
}
}
if (contained == innerWay.getTags().size()) {
BaseTileBasedDataProcessor.this.innerWaysWithoutAdditionalTags.add(innerWay.getId());
}
}
} else {
List waynodeList = new ArrayList<>();
for (TDWay innerSegment : innerSegments) {
if (innerSegment.isReversedInRelation()) {
for (int i = innerSegment.getWayNodes().length - 1; i >= 0; i--) {
waynodeList.add(innerSegment.getWayNodes()[i]);
}
} else {
for (TDNode tdNode : innerSegment.getWayNodes()) {
waynodeList.add(tdNode);
}
}
}
TDNode[] waynodes = waynodeList.toArray(new TDNode[waynodeList.size()]);
// TODO which layer?
innerWay = new TDWay(++BaseTileBasedDataProcessor.this.maxWayID, (byte) 0, null, null, null,
waynodes);
handleVirtualInnerWay(innerWay);
// does not need to be added to corresponding tiles
// virtual inner ways do not have any tags, they are holes in the outer polygon
}
innerList.add(innerWay.getId());
}
}
}
}
protected class WayHandler implements TObjectProcedure {
private long nWays = 0;
@Override
public boolean execute(TDWay way) {
if (way == null) {
return true;
}
if (++this.nWays % 10000 == 0) {
if (progressLogs) {
System.out.print("Progress: Ways " + nfCounts.format(this.nWays)
+ " / " + nfCounts.format(getWaysNumber()) + "\r");
}
}
// we only consider ways that have tags and which have not already
// added as outer way of a relation
// inner ways without additional tags are also not considered as they are processed as part of a
// multi polygon
if (way.isRenderRelevant() && !BaseTileBasedDataProcessor.this.outerToInnerMapping.contains(way.getId())
&& !BaseTileBasedDataProcessor.this.innerWaysWithoutAdditionalTags.contains(way.getId())) {
// TODO #HDstoreData: integrate all way processes from HD and RAM Processor HERE
// e.g. outerToInnerMapping, virtualWays, associatedRelations, implicitRelations
// or declare better that HD Processor does not store object data before writing to file
addImplicitRelationInformation(way);
addWayToTiles(way, BaseTileBasedDataProcessor.this.bboxEnlargement);
}
return true;
}
}
protected static final Logger LOGGER = Logger.getLogger(BaseTileBasedDataProcessor.class.getName());
protected final int bboxEnlargement;
protected final org.mapsforge.core.model.BoundingBox boundingbox;
// accounting
protected float[] countWays;
protected float[] countWayTileFactor;
protected final TShortIntHashMap histogramPoiTags;
protected final TShortIntHashMap histogramWayTags;
protected final TLongSet innerWaysWithoutAdditionalTags;
protected long maxWayID = Long.MIN_VALUE;
protected final TLongObjectHashMap outerToInnerMapping;
protected final List preferredLanguages;
protected final boolean progressLogs;
protected final boolean skipInvalidRelations;
protected final boolean tagValues;
protected TileGridLayout[] tileGridLayouts;
protected final Map tilesToCoastlines;
protected final Map tilesToPartElements;
protected final Map tilesToRootElements;
protected final Map partRootRelations;
// Accounting
private long amountOfNodesProcessed = 0;
private long amountOfRelationsProcessed = 0;
private long amountOfWaysProcessed = 0;
protected final NumberFormat nfCounts = NumberFormat.getInstance();
protected final ZoomIntervalConfiguration zoomIntervalConfiguration;
public BaseTileBasedDataProcessor(MapWriterConfiguration configuration) {
super();
this.boundingbox = configuration.getBboxConfiguration();
this.zoomIntervalConfiguration = configuration.getZoomIntervalConfiguration();
this.tileGridLayouts = new TileGridLayout[this.zoomIntervalConfiguration.getNumberOfZoomIntervals()];
this.bboxEnlargement = configuration.getBboxEnlargement();
this.preferredLanguages = configuration.getPreferredLanguages();
this.progressLogs = configuration.isProgressLogs();
this.skipInvalidRelations = configuration.isSkipInvalidRelations();
this.tagValues = configuration.isTagValues();
this.outerToInnerMapping = new TLongObjectHashMap<>();
this.innerWaysWithoutAdditionalTags = new TLongHashSet();
this.tilesToCoastlines = new HashMap<>();
this.tilesToPartElements = new HashMap<>();
this.tilesToRootElements = new HashMap<>();
this.partRootRelations = new HashMap<>();
this.nfCounts.setGroupingUsed(true);
this.countWays = new float[this.zoomIntervalConfiguration.getNumberOfZoomIntervals()];
this.countWayTileFactor = new float[this.zoomIntervalConfiguration.getNumberOfZoomIntervals()];
this.histogramPoiTags = new TShortIntHashMap();
this.histogramWayTags = new TShortIntHashMap();
// compute horizontal and vertical tile coordinate offsets for all
// base zoom levels
for (int i = 0; i < this.zoomIntervalConfiguration.getNumberOfZoomIntervals(); i++) {
TileCoordinate upperLeft = new TileCoordinate((int) MercatorProjection.longitudeToTileX(
this.boundingbox.minLongitude, this.zoomIntervalConfiguration.getBaseZoom(i)),
(int) MercatorProjection.latitudeToTileY(this.boundingbox.maxLatitude,
this.zoomIntervalConfiguration.getBaseZoom(i)),
this.zoomIntervalConfiguration.getBaseZoom(i));
this.tileGridLayouts[i] = new TileGridLayout(upperLeft, computeNumberOfHorizontalTiles(i),
computeNumberOfVerticalTiles(i));
}
}
@Override
public long cumulatedNumberOfTiles() {
long cumulated = 0;
for (int i = 0; i < this.zoomIntervalConfiguration.getNumberOfZoomIntervals(); i++) {
cumulated += this.tileGridLayouts[i].getAmountTilesHorizontal()
* this.tileGridLayouts[i].getAmountTilesVertical();
}
return cumulated;
}
@Override
public BoundingBox getBoundingBox() {
return this.boundingbox;
}
@Override
public TileGridLayout getTileGridLayout(int zoomIntervalIndex) {
return this.tileGridLayouts[zoomIntervalIndex];
}
@Override
public ZoomIntervalConfiguration getZoomIntervalConfiguration() {
return this.zoomIntervalConfiguration;
}
@Override
public void addNode(Node node) {
if (++this.amountOfNodesProcessed % 1000000 == 0) {
if (this.progressLogs) {
System.out.print("\33[2KAdd nodes: "
+ nfCounts.format(this.amountOfNodesProcessed) + "\r");
}
}
}
@Override
public void addRelation(Relation relation) {
if (++this.amountOfRelationsProcessed % 10000 == 0) {
if (this.progressLogs) {
System.out.print("\33[2KAdd relations: "
+ nfCounts.format(this.amountOfRelationsProcessed) + "\r");
}
}
}
@Override
public void addWay(Way way) {
if (++this.amountOfWaysProcessed % 50000 == 0) {
if (this.progressLogs) {
// "\33[2K" sequence is needed to clear the line, to overwrite larger previous numbers
System.out.print("\33[2KAdd ways: "
+ nfCounts.format(this.amountOfWaysProcessed) + "\r");
}
}
}
@Override
public long getNodesNumber() {
return amountOfNodesProcessed;
}
@Override
public long getRelationsNumber() {
return amountOfRelationsProcessed;
}
@Override
public long getWaysNumber() {
return amountOfWaysProcessed;
}
/**
* Add tags to ways which describe the geo-inheritance.
*
* @param tdWay the way which should get the tags
*/
protected void addImplicitRelationInformation(TDWay tdWay) {
if (!this.tagValues) {
return;
}
// FEATURE Remove this to add id to all elements (increase of space around 1/8)
if (!this.partRootRelations.containsKey(tdWay.getId())) {
return;
}
Long rootId = this.partRootRelations.get(tdWay.getId());
if (rootId == null) {
OSMTagMapping mapping = OSMTagMapping.getInstance();
// Remove accidentally written ids in OSM
for (Short aShort : tdWay.getTags().keySet()) {
if (mapping.getWayTag(aShort).getKey().equals("id")) {
tdWay.getTags().remove(aShort);
break;
}
}
// Add id to root element (respecting tag-mapping file)
OSMTag idTag = mapping.getWayTag("id", String.valueOf(tdWay.getId()));
assert idTag != null;
tdWay.getTags().put(idTag.getId(), OSMUtils.getObjectFromWildcardAndValue(idTag.getValue(), String.valueOf(tdWay.getId())));
} else {
// Add reference id to part element
tdWay.setRef(String.valueOf(rootId));
}
// #HDstoreData: Removing not possible, because HD' tiles need them multiple times (for count and processing)
// partRootRelations.remove(tdWay.getId());
}
protected void addPOI(TDNode poi) {
if (!poi.isPOI()) {
return;
}
byte minZoomLevel = poi.getZoomAppear();
for (int i = 0; i < this.zoomIntervalConfiguration.getNumberOfZoomIntervals(); i++) {
// is POI seen in a zoom interval?
if (minZoomLevel <= this.zoomIntervalConfiguration.getMaxZoom(i)) {
long tileCoordinateX = MercatorProjection.longitudeToTileX(
LatLongUtils.microdegreesToDegrees(poi.getLongitude()),
this.zoomIntervalConfiguration.getBaseZoom(i));
long tileCoordinateY = MercatorProjection.latitudeToTileY(
LatLongUtils.microdegreesToDegrees(poi.getLatitude()),
this.zoomIntervalConfiguration.getBaseZoom(i));
TileData tileData = getTileImpl(i, (int) tileCoordinateX, (int) tileCoordinateY);
if (tileData != null) {
tileData.addPOI(poi);
countPoiTags(poi);
}
}
}
}
protected void addWayToTiles(TDWay way, int enlargement) {
int bboxEnlargementLocal = enlargement;
byte minZoomLevel = way.getMinimumZoomLevel();
for (int i = 0; i < this.zoomIntervalConfiguration.getNumberOfZoomIntervals(); i++) {
// is way seen in a zoom interval?
if (minZoomLevel <= this.zoomIntervalConfiguration.getMaxZoom(i)) {
Set matchedTiles = GeoUtils.mapWayToTiles(way,
this.zoomIntervalConfiguration.getBaseZoom(i), bboxEnlargementLocal);
boolean added = false;
for (TileCoordinate matchedTile : matchedTiles) {
TileData td = getTileImpl(i, matchedTile.getX(), matchedTile.getY());
if (td != null) {
countWayTags(way);
this.countWayTileFactor[i]++;
added = true;
td.addWay(way);
}
}
if (added) {
this.countWays[i]++;
}
}
}
}
/**
* Count tags to optimize order of POI tag data.
*
* @param poi the poi which contains tags
*/
protected void countPoiTags(TDNode poi) {
if (poi == null || poi.getTags() == null) {
return;
}
for (short tag : poi.getTags().keySet()) {
this.histogramPoiTags.adjustOrPutValue(tag, 1, 1);
}
}
protected void countWayTags(Set tags) {
if (tags != null) {
for (short tag : tags) {
this.histogramWayTags.adjustOrPutValue(tag, 1, 1);
}
}
}
/**
* Count tags to optimize order of Way tag data.
*
* @param way the way which contains tags
*/
protected void countWayTags(TDWay way) {
if (way != null) {
countWayTags(way.getTags().keySet());
}
}
protected abstract TileData getTileImpl(int zoom, int tileX, int tileY);
protected abstract void handleAdditionalRelationTags(TDWay virtualWay, TDRelation relation);
/**
* Calculate ids of ways whose polygon inherits part elements.
*/
protected void handleImplicitWayRelations() {
if (!this.tagValues) {
return;
}
/*long progressImplicitRelations = 0;
float limitImplicitRelations = this.tilesToPartElements.entrySet().size();*/
// Iterate through tiles which contain parts
for (Map.Entry tilePartElementEntry : this.tilesToPartElements.entrySet()) {
TLongHashSet tileRootElementSet = this.tilesToRootElements.get(tilePartElementEntry.getKey());
// Continue if no part or no roots are in list; maybe unnecessary
if (tileRootElementSet == null || tileRootElementSet.isEmpty()
|| tilePartElementEntry.getValue().isEmpty()) {
continue;
}
// Log
/*String wayRelLog = "Progress: Implicit relations "
+ ((int) ((progressImplicitRelations / limitImplicitRelations) * 100))
+ "%% - Tile (" + tilePartElementEntry.getKey().getX()
+ ", " + tilePartElementEntry.getKey().getY() + ")";
progressImplicitRelations++;
long nRootElements = 0;*/
// Load parts only once in cache
List pElems = new ArrayList<>();
TLongIterator tilePartElementIterator = tilePartElementEntry.getValue().iterator();
while (tilePartElementIterator.hasNext()) {
TDWay pElem = getWay(tilePartElementIterator.next());
pElems.add(pElem);
}
// Iterate through potential roots
TLongIterator tileRootElementIterator = tileRootElementSet.iterator();
while (tileRootElementIterator.hasNext()) {
/*if(this.progressLogs) {
if (++nRootElements % 1000 == 0) {
System.out.print((wayRelLog + " - Elements " + nfCounts.format(nRootElements) + "\r"));
}
}*/
// Init root element
TDWay rElem = getWay(tileRootElementIterator.next());
BoundingBox rBox = GeoUtils.mapWayToBoundingBox(rElem);
if (rBox == null) {
continue;
}
Polygon rPolygon = null; // Lazy initialization, because root may not be needed
// Iterate through parts
for (int i = pElems.size() - 1; i >= 0; i--) {
TDWay pElem = pElems.get(i);
// Exclude most elements with bounding box
if (pElem.getWayNodes().length < 3) {
continue;
}
if (!rBox.contains(LatLongUtils.microdegreesToDegrees(pElem.getWayNodes()[0].getLatitude()),
LatLongUtils.microdegreesToDegrees(pElem.getWayNodes()[0].getLongitude()))) {
continue;
}
// Now calculate exact polygons
Polygon pPolygon = GeoUtils.mapWayToPolygon(pElem);
if (pPolygon == null) {
continue;
}
if (rPolygon == null) {
rPolygon = GeoUtils.mapWayToPolygon(rElem);
if (rPolygon == null) {
continue;
}
}
/* Use interior point cause
* - boundary points may intersect with other geometries
* - part polygon often not completely covered by root polygon
*/
if (rPolygon.covers(pPolygon.getInteriorPoint())) {
// Memorize multi-polygons
if (!this.partRootRelations.containsKey(rElem.getId())) {
this.partRootRelations.put(rElem.getId(), null); // Add root element
}
this.partRootRelations.put(pElem.getId(), rElem.getId()); // Add part element
// Remove part which is already referenced
pElems.remove(pElem);
tilePartElementEntry.getValue().remove(pElem.getId());
}
}
}
}
LOGGER.info("calculated " + nfCounts.format(partRootRelations.size()) + " implicit relations");
}
protected abstract void handleVirtualInnerWay(TDWay virtualWay);
protected abstract void handleVirtualOuterWay(TDWay virtualWay);
private int computeNumberOfHorizontalTiles(int zoomIntervalIndex) {
long tileCoordinateLeft = MercatorProjection.longitudeToTileX(this.boundingbox.minLongitude,
this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex));
long tileCoordinateRight = MercatorProjection.longitudeToTileX(this.boundingbox.maxLongitude,
this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex));
assert tileCoordinateLeft <= tileCoordinateRight;
assert tileCoordinateLeft - tileCoordinateRight + 1 < Integer.MAX_VALUE;
LOGGER.finer("basezoom: " + this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex) + "\t+n_horizontal: "
+ (tileCoordinateRight - tileCoordinateLeft + 1));
return (int) (tileCoordinateRight - tileCoordinateLeft + 1);
}
private int computeNumberOfVerticalTiles(int zoomIntervalIndex) {
long tileCoordinateBottom = MercatorProjection.latitudeToTileY(this.boundingbox.minLatitude,
this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex));
long tileCoordinateTop = MercatorProjection.latitudeToTileY(this.boundingbox.maxLatitude,
this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex));
assert tileCoordinateBottom >= tileCoordinateTop;
assert tileCoordinateBottom - tileCoordinateTop + 1 <= Integer.MAX_VALUE;
LOGGER.finer("basezoom: " + this.zoomIntervalConfiguration.getBaseZoom(zoomIntervalIndex) + "\t+n_vertical: "
+ (tileCoordinateBottom - tileCoordinateTop + 1));
return (int) (tileCoordinateBottom - tileCoordinateTop + 1);
}
/**
* Prepare relations which aren't written as relation and only exist as geo-inheritance.
* Root and part elements will be assigned to a tile (not to be confused with later tile processing).
* This facilitates to find matching parts and accelerates the process.
*
* Coastlines are handled too, for simplicity reasons.
*
* @param tdWay the way, which should be prepared
*/
protected void prepareImplicitWayRelations(TDWay tdWay) {
if (tdWay.isCoastline()) {
// find matching tiles on zoom level 12
Set coastLineTiles = GeoUtils.mapWayToTiles(tdWay, TileInfo.TILE_INFO_ZOOMLEVEL, 0);
for (TileCoordinate tileCoordinate : coastLineTiles) {
TLongHashSet coastlines = this.tilesToCoastlines.get(tileCoordinate);
if (coastlines == null) {
coastlines = new TLongHashSet();
this.tilesToCoastlines.put(tileCoordinate, coastlines);
}
coastlines.add(tdWay.getId());
}
} else if (this.tagValues) {
if (tdWay.isRootElement()) {
Set rootTiles = GeoUtils.mapWayToTiles(tdWay, TileInfo.TILE_INFO_ZOOMLEVEL, 0);
for (TileCoordinate tileCoordinate : rootTiles) {
TLongHashSet roots = this.tilesToRootElements.get(tileCoordinate);
if (roots == null) {
roots = new TLongHashSet();
this.tilesToRootElements.put(tileCoordinate, roots);
}
roots.add(tdWay.getId());
}
} else if (tdWay.isPartElement()) {
Set partTiles = GeoUtils.mapWayToTiles(tdWay, TileInfo.TILE_INFO_ZOOMLEVEL, 0);
for (TileCoordinate tileCoordinate : partTiles) {
TLongHashSet parts = this.tilesToPartElements.get(tileCoordinate);
if (parts == null) {
parts = new TLongHashSet();
this.tilesToPartElements.put(tileCoordinate, parts);
}
parts.add(tdWay.getId());
}
}
}
}
}