Building layers: use ZoomLimiter for overzoom (#528)

This commit is contained in:
Gustl22 2018-04-15 18:58:50 +02:00 committed by Emux
parent 7a9fc99079
commit 472a34c280
No known key found for this signature in database
GPG Key ID: 64ED9980896038C3
7 changed files with 227 additions and 33 deletions

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2018 devemux86
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -34,6 +35,8 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.oscim.layers.tile.MapTile.State.CANCEL;
import static org.oscim.layers.tile.MapTile.State.DEADBEEF;
@ -53,9 +56,15 @@ public class TileManager {
private final int mCacheLimit;
private int mCacheReduce;
private int mMinZoom;
int mMinZoom;
private int mMaxZoom;
/**
* Collection of zoom limits, useful for some layers.
* Use <code>List</code> as need to allow duplicates.
*/
private final List<Integer> mZoomLimits = new ArrayList<>();
private int[] mZoomTable;
/**
@ -304,6 +313,17 @@ public class TileManager {
/* scan visible tiles. callback function calls 'addTile'
* which updates mNewTiles */
mNewTiles.cnt = 0;
// Retrieve tiles needed for layers to multiple rendering
for (int i = 0; i < mZoomLimits.size(); i++) {
int zoom = mZoomLimits.get(i);
if (i > 0 && zoom == mZoomLimits.get(i - 1))
continue;
if (tileZoom > zoom)
mScanBox.scan(pos.x, pos.y, pos.scale, zoom, mMapPlane);
}
// Retrieve regular tiles at tile zoom
mScanBox.scan(pos.x, pos.y, pos.scale, tileZoom, mMapPlane);
MapTile[] newTiles = mNewTiles.tiles;
@ -392,7 +412,7 @@ public class TileManager {
}
/**
* Retrive a TileSet of current tiles. Tiles remain locked in cache until
* Retrieve a TileSet of current tiles. Tiles remain locked in cache until
* the set is unlocked by either passing it again to this function or to
* releaseTiles.
*
@ -729,4 +749,22 @@ public class TileManager {
mMinZoom = zoomLevelMin;
mMaxZoom = zoomLevelMax;
}
/**
* Allow loading tiles at specified zoom if higher zoom levels are requested.
*/
public void addZoomLimit(int zoomLimit) {
if (zoomLimit > mMinZoom && zoomLimit < mMaxZoom) {
mZoomLimits.add(zoomLimit);
Collections.sort(mZoomLimits);
}
}
/**
* Remove zoom limit.
* Tiles of that zoom level won't be requested anymore on higher zoom levels.
*/
public void removeZoomLimit(int zoomLimit) {
mZoomLimits.remove(Integer.valueOf(zoomLimit));
}
}

View File

@ -205,18 +205,29 @@ public abstract class TileRenderer extends LayerRenderer {
private final Object tilelock = new Object();
/**
* Update tileSet with currently visible tiles get a TileSet of currently
* visible tiles
* Update tileSet with currently visible tiles to get a TileSet of currently visible tiles.
*/
public boolean getVisibleTiles(TileSet tileSet) {
return getVisibleTiles(tileSet, false) != null;
}
/**
* Update tileSet with currently visible tiles to get a TileSet of currently visible tiles.
* Replace tiles with ancestor (parent, etc.) if tiles are not loaded yet.
*
* @return original zoom level, otherwise null (if nothing can be loaded)
*/
public Integer getVisibleTiles(TileSet tileSet, boolean replace) {
if (tileSet == null)
return false;
return null;
if (mDrawTiles == null) {
releaseTiles(tileSet);
return false;
return null;
}
Integer zoom = null;
int prevSerial = tileSet.serial;
/* ensure tiles keep visible state */
@ -238,19 +249,37 @@ public abstract class TileRenderer extends LayerRenderer {
t.lock();
}
// Set main zoom level, even if no tiles are ready
if (cnt > 0)
zoom = (int) newTiles[0].zoomLevel;
/* unlock previous tiles */
tileSet.releaseTiles();
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state(READY))
tileSet.tiles[tileSet.cnt++] = t;
if (t.isVisible) {
if (t.state(READY))
tileSet.tiles[tileSet.cnt++] = t;
else if (replace) {
// Replace with next available ancestor
for (int j = t.zoomLevel - 1; j > mTileManager.mMinZoom; j--) {
int diff = t.zoomLevel - j;
MapTile parent = mTileManager.getTile(t.tileX >> diff, t.tileY >> diff, j);
if (parent != null && parent.state(READY)) {
parent.lock();
tileSet.tiles[tileSet.cnt++] = parent;
break;
}
}
}
}
}
tileSet.serial = mUploadSerial;
}
return prevSerial != tileSet.serial;
return prevSerial != tileSet.serial ? zoom : null;
}
public void releaseTiles(TileSet tileSet) {

View File

@ -0,0 +1,90 @@
/*
* Copyright 2018 Gustl22
*
* 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.layers.tile;
public class ZoomLimiter {
private final int mMinZoom;
private final int mMaxZoom;
private final TileManager mTileManager;
/**
* Indicates that layer isn't processed again over zoom limit.
*/
private final int mZoomLimit;
/**
* Layer would avoid rendering tiles over a specific zoom limit.
*/
public ZoomLimiter(TileManager tileManager, int minZoom, int maxZoom, int zoomLimit) {
if (zoomLimit < minZoom || zoomLimit > maxZoom) {
throw new IllegalArgumentException("Zoom limit is out of range");
}
mTileManager = tileManager;
mMinZoom = minZoom;
mMaxZoom = maxZoom;
mZoomLimit = zoomLimit;
}
public void addZoomLimit() {
if (mZoomLimit < mMaxZoom)
mTileManager.addZoomLimit(mZoomLimit);
}
public int getMaxZoom() {
return mMaxZoom;
}
public int getMinZoom() {
return mMinZoom;
}
/**
* Get tile of zoom limit if zoom level is larger than limit.
*/
public MapTile getTile(MapTile t) {
if (t.zoomLevel > mZoomLimit && t.zoomLevel <= mMaxZoom) {
int diff = t.zoomLevel - mZoomLimit;
return mTileManager.getTile(t.tileX >> diff, t.tileY >> diff, mZoomLimit);
}
return t;
}
public TileManager getTileManager() {
return mTileManager;
}
public int getZoomLimit() {
return mZoomLimit;
}
public void removeZoomLimit() {
if (mZoomLimit < mMaxZoom)
mTileManager.removeZoomLimit(mZoomLimit);
}
public interface IZoomLimiter {
/**
* Add zoom limit to tile manager to load these tiles.
*/
void addZoomLimit();
/**
* Remove zoom limit from tile manager.
*/
void removeZoomLimit();
}
}

View File

@ -23,6 +23,7 @@ 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;
@ -39,12 +40,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class BuildingLayer extends Layer implements TileLoaderThemeHook {
public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLimiter.IZoomLimiter {
protected final static int BUILDING_LEVEL_HEIGHT = 280; // cm
public final static int MIN_ZOOM = 17;
public final static int MAX_ZOOM = 17;
public final static int MAX_ZOOM = 17; // TODO use Viewport.MAX_ZOOM_LEVEL;
public static boolean POST_AA = false;
public static boolean TRANSLUCENT = true;
@ -54,6 +55,8 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
// Can be replaced with Multimap in Java 8
protected java.util.Map<Integer, List<BuildingElement>> mBuildings = new HashMap<>();
private final ZoomLimiter mZoomLimiter;
class BuildingElement {
MapElement element;
ExtrusionStyle style;
@ -78,13 +81,25 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
tileLayer.addHook(this);
mRenderer = new BuildingRenderer(tileLayer.tileRenderer(),
zoomMin, zoomMax,
// Use zoomMin as zoomLimit to render buildings only once
mZoomLimiter = new ZoomLimiter(tileLayer.getManager(), zoomMin, zoomMax, zoomMin);
mRenderer = new BuildingRenderer(tileLayer.tileRenderer(), mZoomLimiter,
mesh, !mesh && TRANSLUCENT); // alpha must be disabled for mesh renderer
if (POST_AA)
mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
}
@Override
public void addZoomLimit() {
mZoomLimiter.addZoomLimit();
}
@Override
public void removeZoomLimit() {
mZoomLimiter.removeZoomLimit();
}
/**
* TileLoaderThemeHook
*/
@ -95,6 +110,8 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
if (!(style instanceof ExtrusionStyle))
return false;
if (tile.zoomLevel > mZoomLimiter.getZoomLimit())
return false;
ExtrusionStyle extrusion = (ExtrusionStyle) style.current();

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2018 devemux86
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -21,6 +22,7 @@ import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileDistanceSort;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.layers.tile.TileSet;
import org.oscim.layers.tile.ZoomLimiter;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
@ -29,6 +31,9 @@ import org.oscim.renderer.bucket.RenderBuckets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
import static java.lang.System.currentTimeMillis;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY;
@ -40,8 +45,7 @@ public class BuildingRenderer extends ExtrusionRenderer {
private final TileRenderer mTileRenderer;
private final TileSet mTileSet;
private final int mZoomMin;
private final int mZoomMax;
private final ZoomLimiter mZoomLimiter;
private final float mFadeInTime = 250;
private final float mFadeOutTime = 400;
@ -49,12 +53,11 @@ public class BuildingRenderer extends ExtrusionRenderer {
private long mAnimTime;
private boolean mShow;
public BuildingRenderer(TileRenderer tileRenderer, int zoomMin, int zoomMax,
public BuildingRenderer(TileRenderer tileRenderer, ZoomLimiter zoomLimiter,
boolean mesh, boolean alpha) {
super(mesh, alpha);
mZoomMax = zoomMax;
mZoomMin = zoomMin;
mZoomLimiter = zoomLimiter;
mTileRenderer = tileRenderer;
mTileSet = new TileSet();
}
@ -69,7 +72,7 @@ public class BuildingRenderer extends ExtrusionRenderer {
@Override
public void update(GLViewport v) {
int diff = (v.pos.zoomLevel - mZoomMin);
int diff = (v.pos.zoomLevel - mZoomLimiter.getMinZoom());
/* if below min zoom or already faded out */
if (diff < -1) {
@ -106,9 +109,9 @@ public class BuildingRenderer extends ExtrusionRenderer {
return;
}
mTileRenderer.getVisibleTiles(mTileSet);
Integer zoom = mTileRenderer.getVisibleTiles(mTileSet, true);
if (mTileSet.cnt == 0) {
if (mTileSet.cnt == 0 || zoom == null) {
mTileRenderer.releaseTiles(mTileSet);
setReady(false);
return;
@ -126,9 +129,8 @@ public class BuildingRenderer extends ExtrusionRenderer {
boolean compiled = false;
int activeTiles = 0;
int zoom = tiles[0].zoomLevel;
if (zoom >= mZoomMin && zoom <= mZoomMax) {
if (zoom >= mZoomLimiter.getMinZoom() && zoom <= mZoomLimiter.getZoomLimit()) {
/* TODO - if tile is not available try parent or children */
for (int i = 0; i < mTileSet.cnt; i++) {
@ -143,14 +145,15 @@ public class BuildingRenderer extends ExtrusionRenderer {
compiled = true;
}
}
}
/*else if (zoom == mZoomMax + 1) {
// special case for s3db: render from parent tiles
} else if (zoom > mZoomLimiter.getZoomLimit() && zoom <= mZoomLimiter.getMaxZoom()) {
// render from zoom limit tiles (avoid duplicates and null)
Set<MapTile> hashTiles = new HashSet<>();
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i].node.parent();
MapTile t = mZoomLimiter.getTile(tiles[i]);
if (t == null)
continue;
if (!hashTiles.add(t))
continue;
ExtrusionBuckets ebs = getBuckets(t);
if (ebs == null)
@ -164,8 +167,7 @@ public class BuildingRenderer extends ExtrusionRenderer {
compiled = true;
}
}
}*/
else if (zoom == mZoomMin - 1) {
} else if (zoom == mZoomLimiter.getMinZoom() - 1) {
/* check if proxy children are ready */
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i];

View File

@ -19,6 +19,7 @@ package org.oscim.layers.tile.buildings;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.layers.tile.ZoomLimiter;
import org.oscim.map.Map;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
@ -49,7 +50,7 @@ public class S3DBTileLayer extends TileLayer {
*/
public S3DBTileLayer(Map map, TileSource tileSource, boolean fxaa, boolean ssao) {
super(map, new TileManager(map, MAX_CACHE));
setRenderer(new S3DBTileRenderer(fxaa, ssao));
setRenderer(new S3DBTileRenderer(mTileManager, fxaa, ssao));
mTileManager.setZoomLevel(MIN_ZOOM, MAX_ZOOM);
mTileSource = tileSource;
@ -64,8 +65,8 @@ public class S3DBTileLayer extends TileLayer {
public static class S3DBTileRenderer extends TileRenderer {
LayerRenderer mRenderer;
public S3DBTileRenderer(boolean fxaa, boolean ssao) {
mRenderer = new BuildingRenderer(this, MIN_ZOOM, MAX_ZOOM, true, false);
public S3DBTileRenderer(TileManager manager, boolean fxaa, boolean ssao) {
mRenderer = new BuildingRenderer(this, new ZoomLimiter(manager, MIN_ZOOM, MAX_ZOOM, MIN_ZOOM), true, false);
if (fxaa || ssao) {
Mode mode = Mode.FXAA;

View File

@ -3,6 +3,7 @@
* Copyright 2016-2017 devemux86
* Copyright 2016 Andrey Novikov
* Copyright 2017 Longri
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -24,6 +25,7 @@ import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
import org.oscim.layers.GroupLayer;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.ZoomLimiter;
import org.oscim.map.Map.InputListener;
import org.oscim.map.Map.UpdateListener;
import org.oscim.renderer.LayerRenderer;
@ -78,6 +80,9 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.bind((UpdateListener) layer);
if (layer instanceof InputListener)
mMap.input.bind((InputListener) layer);
// add zoom limit to tile manager
if (layer instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) layer).addZoomLimit();
// bind added group layer
if (layer instanceof GroupLayer) {
@ -87,6 +92,8 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.bind((UpdateListener) gl);
if (gl instanceof InputListener)
mMap.input.bind((InputListener) gl);
if (gl instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) gl).addZoomLimit();
}
}
@ -128,6 +135,9 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.unbind((UpdateListener) remove);
if (remove instanceof InputListener)
mMap.input.unbind((InputListener) remove);
// remove zoom limit from tile manager
if (remove instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) remove).removeZoomLimit();
// unbind removed group layer
if (remove instanceof GroupLayer) {
@ -137,6 +147,8 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.unbind((UpdateListener) gl);
if (gl instanceof InputListener)
mMap.input.unbind((InputListener) gl);
if (gl instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) gl).removeZoomLimit();
}
}
@ -164,6 +176,9 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.unbind((UpdateListener) remove);
if (remove instanceof InputListener)
mMap.input.unbind((InputListener) remove);
// remove zoom limit from tile manager
if (remove instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) remove).removeZoomLimit();
// unbind replaced group layer
if (remove instanceof GroupLayer) {
@ -173,6 +188,8 @@ public final class Layers extends AbstractList<Layer> {
mMap.events.unbind((UpdateListener) gl);
if (gl instanceof InputListener)
mMap.input.unbind((InputListener) gl);
if (gl instanceof ZoomLimiter.IZoomLimiter)
((ZoomLimiter.IZoomLimiter) gl).removeZoomLimit();
}
}