diff --git a/vtm/src/org/oscim/core/MapElement.java b/vtm/src/org/oscim/core/MapElement.java index b3690e62..47ae5fbe 100644 --- a/vtm/src/org/oscim/core/MapElement.java +++ b/vtm/src/org/oscim/core/MapElement.java @@ -1,6 +1,7 @@ /* * Copyright 2012 Hannes Janetzek * Copyright 2016 Andrey Novikov + * Copyright 2017 Gustl22 * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * @@ -17,6 +18,8 @@ */ package org.oscim.core; +import java.util.Arrays; + /** * The MapElement class is a reusable containter for a geometry * with tags. @@ -65,4 +68,20 @@ public class MapElement extends GeometryBuffer { return tags.toString() + '\n' + super.toString() + '\n'; } + + /** + * @return a deep copy of this MapElement + */ + public MapElement clone() { + MapElement copy = new MapElement(); + copy.tags.set(this.tags.asArray()); + copy.points = Arrays.copyOf(this.points, this.points.length); + copy.pointPos = this.pointPos; + copy.labelPosition = this.labelPosition; + copy.setLayer(this.layer); + copy.index = Arrays.copyOf(this.index, this.index.length); + copy.indexPos = this.indexPos; + copy.type = this.type; + return copy; + } } diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java index 9ada4383..d3df2ea6 100644 --- a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java +++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java @@ -36,6 +36,12 @@ import org.oscim.theme.styles.ExtrusionStyle; import org.oscim.theme.styles.RenderStyle; import org.oscim.utils.pool.Inlist; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class BuildingLayer extends Layer implements TileLoaderThemeHook { private final static int BUILDING_LEVEL_HEIGHT = 280; // cm @@ -48,6 +54,21 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { private static final Object BUILDING_DATA = BuildingLayer.class.getName(); + // Can replace with Multimap in Java 8 + private HashMap> mBuildings = new HashMap<>(); + + class BuildingElement { + MapElement element; + ExtrusionStyle style; + boolean isPart; + + BuildingElement(MapElement element, ExtrusionStyle style, boolean isPart) { + this.element = element; + this.style = style; + this.isPart = isPart; + } + } + public BuildingLayer(Map map, VectorTileLayer tileLayer) { this(map, tileLayer, MIN_ZOOM, MAX_ZOOM); } @@ -77,6 +98,36 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { ExtrusionStyle extrusion = (ExtrusionStyle) style.current(); + // Filter all building elements + // TODO #TagFromTheme: load from theme or decode tags to generalize mapsforge tags + boolean isBuildingPart = element.tags.containsKey(Tag.KEY_BUILDING_PART) + || (element.tags.containsKey("kind") && element.tags.getValue("kind").equals("building_part")); // Mapzen + if (element.tags.containsKey(Tag.KEY_BUILDING) || isBuildingPart + || (element.tags.containsKey("kind") && element.tags.getValue("kind").equals("building"))) { // Mapzen + List buildingElements = mBuildings.get(tile.hashCode()); + if (buildingElements == null) { + buildingElements = new ArrayList<>(); + mBuildings.put(tile.hashCode(), buildingElements); + } + element = element.clone(); // Deep copy, because element will be cleared + buildingElements.add(new BuildingElement(element, extrusion, isBuildingPart)); + return true; + } + + // Process other elements immediately + processElement(element, extrusion, tile); + + return true; + } + + /** + * Process map element. + * + * @param element the map element + * @param extrusion the style of map element + * @param tile the tile which contains map element + */ + private void processElement(MapElement element, ExtrusionStyle extrusion, MapTile tile) { int height = 0; // cm int minHeight = 0; // cm @@ -84,7 +135,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { if (v != null) height = (int) (Float.parseFloat(v) * 100); else { - // FIXME load from theme or decode tags to generalize level/height tags + // #TagFromTheme: generalize level/height tags if ((v = element.tags.getValue(Tag.KEY_BUILDING_LEVELS)) != null) height = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT); } @@ -92,9 +143,13 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { v = element.tags.getValue(Tag.KEY_MIN_HEIGHT); if (v != null) minHeight = (int) (Float.parseFloat(v) * 100); + else { + // #TagFromTheme: level/height tags + if ((v = element.tags.getValue(Tag.KEY_BUILDING_MIN_LEVEL)) != null) + minHeight = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT); + } if (height == 0) - // FIXME ignore buildings containing building parts height = extrusion.defaultHeight * 100; ExtrusionBuckets ebs = get(tile); @@ -102,7 +157,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { for (ExtrusionBucket b = ebs.buckets; b != null; b = b.next()) { if (b.colors == extrusion.colors) { b.add(element, height, minHeight); - return true; + return; } } @@ -115,8 +170,45 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { extrusion.colors)); ebs.buckets.add(element, height, minHeight); + } - return true; + /** + * Process all stored map elements (here only buildings). + * + * @param tile the tile which contains stored map elements + */ + private void processElements(MapTile tile) { + if (!mBuildings.containsKey(tile.hashCode())) + return; + + List tileBuildings = mBuildings.get(tile.hashCode()); + Set rootBuildings = new HashSet<>(); + for (BuildingElement partBuilding : tileBuildings) { + if (!partBuilding.isPart) + continue; + + String refId = partBuilding.element.tags.getValue(Tag.KEY_REF); // #TagFromTheme + refId = refId == null ? partBuilding.element.tags.getValue("root_id") : refId; // Mapzen + if (refId == null) + continue; + + // Search buildings which inherit parts + for (BuildingElement rootBuilding : tileBuildings) { + if (rootBuilding.isPart + || !(refId.equals(rootBuilding.element.tags.getValue(Tag.KEY_ID)))) + continue; + + rootBuildings.add(rootBuilding); + break; + } + } + + tileBuildings.removeAll(rootBuildings); // root buildings aren't rendered + + for (BuildingElement buildingElement : tileBuildings) { + processElement(buildingElement.element, buildingElement.style, tile); + } + mBuildings.remove(tile.hashCode()); } public static ExtrusionBuckets get(MapTile tile) { @@ -130,9 +222,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook { @Override public void complete(MapTile tile, boolean success) { - if (success) + if (success) { + processElements(tile); get(tile).prepare(); - else + } else get(tile).setBuckets(null); }