All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.oscim.layers.tile.buildings.BuildingLayer Maven / Gradle / Ivy

Go to download

OpenGL vector map library written in Java - running on Android, iOS, Desktop and within the browser.

There is a newer version: 0.22.0
Show newest version
/*
 * Copyright 2013 Hannes Janetzek
 * Copyright 2016-2019 devemux86
 * Copyright 2016 Robin Boldt
 * Copyright 2017-2019 Gustl22
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see .
 */
package org.oscim.layers.tile.buildings;

import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.Platform;
import org.oscim.core.MapElement;
import org.oscim.core.Tag;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.ZoomLimiter;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
import org.oscim.map.Map;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.OffscreenRenderer;
import org.oscim.renderer.OffscreenRenderer.Mode;
import org.oscim.renderer.bucket.ExtrusionBuckets;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.renderer.light.ShadowRenderer;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.utils.geom.GeometryUtils;

import java.util.*;

public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLimiter.IZoomLimiter {

    protected static final int BUILDING_LEVEL_HEIGHT = 280; // cm

    public static final int MIN_ZOOM = 17;

    /**
     * Use Fast Approximate Anti-Aliasing (FXAA) and Screen Space Ambient Occlusion (SSAO).
     */
    public static boolean POST_AA = false;

    /**
     * Use real time calculations to pre-process data.
     */
    public static boolean RAW_DATA = false;

    /**
     * Let vanish extrusions / meshes which are covered by others.
     * {@link org.oscim.renderer.bucket.RenderBucket#EXTRUSION}: roofs are always translucent.
     * 

* To better notice the difference, reduce the alpha value of extrusion colors in themes. */ public static boolean TRANSLUCENT = true; private static final Object BUILDING_DATA = BuildingLayer.class.getName(); // Can be replaced with Multimap in Java 8 protected java.util.Map> mBuildings = new HashMap<>(); protected final ExtrusionRenderer mExtrusionRenderer; private final ZoomLimiter mZoomLimiter; protected final VectorTileLayer mTileLayer; class BuildingElement { MapElement element; ExtrusionStyle style; BuildingElement(MapElement element, ExtrusionStyle style) { this.element = element; this.style = style; } } public BuildingLayer(Map map, VectorTileLayer tileLayer) { this(map, tileLayer, false, false); } public BuildingLayer(Map map, VectorTileLayer tileLayer, boolean mesh, boolean shadow) { this(map, tileLayer, MIN_ZOOM, map.viewport().getMaxZoomLevel(), mesh, shadow); } /** * @param map The map data to add * @param tileLayer The vector tile layer which contains the tiles and the map elements * @param zoomMin The minimum zoom at which the layer appears * @param zoomMax The maximum zoom at which the layer appears * @param mesh Declare if using mesh or polygon renderer * @param shadow Declare if using shadow renderer */ public BuildingLayer(Map map, VectorTileLayer tileLayer, int zoomMin, int zoomMax, boolean mesh, boolean shadow) { super(map); mTileLayer = tileLayer; mTileLayer.addHook(this); // Use zoomMin as zoomLimit to render buildings only once mZoomLimiter = new ZoomLimiter(tileLayer.getManager(), zoomMin, zoomMax, zoomMin); // Buildings translucency does not work on macOS, see #61 if (CanvasAdapter.platform == Platform.MACOS) TRANSLUCENT = false; mRenderer = mExtrusionRenderer = new BuildingRenderer(tileLayer.tileRenderer(), mZoomLimiter, mesh, TRANSLUCENT); // TODO Allow shadow and POST_AA at same time if (shadow) mRenderer = new ShadowRenderer(mExtrusionRenderer); else if (POST_AA) mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer); } @Override public void addZoomLimit() { mZoomLimiter.addZoomLimit(); } @Override public void removeZoomLimit() { mZoomLimiter.removeZoomLimit(); } /** * TileLoaderThemeHook */ @Override public boolean process(MapTile tile, RenderBuckets buckets, MapElement element, RenderStyle style, int level) { // FIXME artifacts at tile borders in last extraction zoom as they're clipped if (!(style instanceof ExtrusionStyle)) return false; if (tile.zoomLevel > mZoomLimiter.getZoomLimit()) return false; ExtrusionStyle extrusion = (ExtrusionStyle) style.current(); // Filter all building elements // TODO #TagFromTheme: load from theme or decode tags to generalize mapsforge tags if (element.isBuilding() || element.isBuildingPart()) { List buildingElements = mBuildings.get(tile.hashCode()); if (buildingElements == null) { buildingElements = new ArrayList<>(); mBuildings.put(tile.hashCode(), buildingElements); } element = new MapElement(element); // Deep copy, because element will be cleared if (RAW_DATA && element.isClockwise() < 0) { // Buildings must be counter clockwise in VTM (mirrored to OSM) element.reverse(); } buildingElements.add(new BuildingElement(element, extrusion)); 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 */ protected void processElement(MapElement element, ExtrusionStyle extrusion, MapTile tile) { int height = 0; // cm int minHeight = 0; // cm Float f = element.getHeight(mTileLayer.getTheme()); if (f != null) height = (int) (f * 100); else { // #TagFromTheme: generalize level/height tags String v = getValue(element, Tag.KEY_BUILDING_LEVELS); if (v != null) height = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT); } f = element.getMinHeight(mTileLayer.getTheme()); if (f != null) minHeight = (int) (f * 100); else { // #TagFromTheme: level/height tags String v = getValue(element, Tag.KEY_BUILDING_MIN_LEVEL); if (v != null) minHeight = (int) (Float.parseFloat(v) * BUILDING_LEVEL_HEIGHT); } if (height == 0) height = extrusion.defaultHeight * 100; ExtrusionBuckets ebs = get(tile); ebs.addPolyElement(element, tile.getGroundScale(), extrusion.colors, height, minHeight); } /** * Process all stored map elements (here only buildings). * * @param tile the tile which contains stored map elements */ protected 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.element.isBuildingPart()) continue; String refId = getValue(partBuilding.element, Tag.KEY_REF); if (refId == null) continue; // Search buildings which inherit parts for (BuildingElement rootBuilding : tileBuildings) { if (rootBuilding.element.isBuildingPart()) continue; if (RAW_DATA) { float[] center = GeometryUtils.center(partBuilding.element.points, 0, partBuilding.element.pointNextPos, null); if (!GeometryUtils.pointInPoly(center[0], center[1], rootBuilding.element.points, rootBuilding.element.index[0], 0)) continue; } else if (!(refId.equals(getValue(rootBuilding.element, 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()); } /** * @param tile the MapTile * @return ExtrusionBuckets of the tile */ public static ExtrusionBuckets get(MapTile tile) { ExtrusionBuckets ebs = (ExtrusionBuckets) tile.getData(BUILDING_DATA); if (ebs == null) { ebs = new ExtrusionBuckets(tile); tile.addData(BUILDING_DATA, ebs); } return ebs; } /** * Get the ExtrusionRenderer for customization. */ public ExtrusionRenderer getExtrusionRenderer() { return mExtrusionRenderer; } /** * @return the tile source tag key or library tag key as fallback */ protected String getKeyOrDefault(String key) { if (mTileLayer.getTheme() == null) return key; String res = mTileLayer.getTheme().transformBackwardKey(key); return res != null ? res : key; } /** * Get the forward transformed value from tile source tag via the library tag key. * * @param key the library tag key * @return the tile source tag value transformed to library tag value */ protected String getTransformedValue(MapElement element, String key) { if (mTileLayer.getTheme() == null) return element.tags.getValue(key); /* Get tile source key of specified lib key from theme or fall back to lib key */ key = getKeyOrDefault(key); /* Get element tag with tile source key, if exists */ Tag tsTag = element.tags.get(key); if (tsTag == null) return null; /* Transform tile source tag to lib tag */ Tag libTag = mTileLayer.getTheme().transformForwardTag(tsTag); if (libTag != null) return libTag.value; /* Use tile source value, if transformation rule not exists */ return tsTag.value; } /** * Get the tile source tag value via the library tag key. * * @param key the library tag key * @return the tile source tag value of specified library tag key */ protected String getValue(MapElement element, String key) { return element.tags.getValue(getKeyOrDefault(key)); } @Override public void complete(MapTile tile, boolean success) { if (success) { processElements(tile); get(tile).prepare(); } else get(tile).resetBuckets(null); } // private int multi; // @Override // public void onInputEvent(Event event, MotionEvent e) { // int action = e.getAction() & MotionEvent.ACTION_MASK; // if (action == MotionEvent.ACTION_POINTER_DOWN) { // multi++; // } else if (action == MotionEvent.ACTION_POINTER_UP) { // multi--; // if (!mActive && mAlpha > 0) { // // finish hiding // //log.debug("add multi hide timer " + mAlpha); // addShowTimer(mFadeTime * mAlpha, false); // } // } else if (action == MotionEvent.ACTION_CANCEL) { // multi = 0; // log.debug("cancel " + multi); // if (mTimer != null) { // mTimer.cancel(); // mTimer = null; // } // } // } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy