![JAR search and dependency download from the Maven repository](/logo.png)
org.oscim.layers.tile.TileRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vtm Show documentation
Show all versions of vtm Show documentation
OpenGL vector map library written in Java - running on Android, iOS, Desktop and within the browser.
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2018 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;
import org.oscim.layers.tile.MapTile.TileNode;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.utils.ScanBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.oscim.layers.tile.MapTile.PROXY_PARENT;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY;
public abstract class TileRenderer extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(TileRenderer.class);
/**
* fade-in time
*/
protected static final float FADE_TIME = 500;
protected static final int MAX_TILE_LOAD = 8;
private TileManager mTileManager;
protected final TileSet mDrawTiles;
protected int mProxyTileCnt;
private int mOverdraw = 0;
private float mAlpha = 1;
protected int mOverdrawColor;
protected float mLayerAlpha;
private int mUploadSerial;
public TileRenderer() {
mUploadSerial = 0;
mDrawTiles = new TileSet();
}
protected void setTileManager(TileManager tileManager) {
mTileManager = tileManager;
}
/**
* Threadsafe
*/
public synchronized void setOverdrawColor(int color) {
mOverdraw = color;
}
/**
* Threadsafe
*/
public synchronized void setBitmapAlpha(float alpha) {
mAlpha = alpha;
}
/**
* synced with clearTiles, setOverdrawColor and setBitmapAlpha
*/
@Override
public synchronized void update(GLViewport v) {
/* count placeholder tiles */
if (mAlpha == 0) {
mDrawTiles.releaseTiles();
setReady(false);
return;
}
/* keep constant while rendering frame */
mLayerAlpha = mAlpha;
mOverdrawColor = mOverdraw;
/* get current tiles to draw */
synchronized (tilelock) {
boolean tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
if (mDrawTiles.cnt == 0) {
setReady(false);
mProxyTileCnt = 0;
return;
}
/* update isVisible flag true for tiles that intersect view */
if (tilesChanged || v.changed()) {
/* lock tiles while updating isVisible state */
mProxyTileCnt = 0;
MapTile[] tiles = mDrawTiles.tiles;
int tileZoom = tiles[0].zoomLevel;
for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false;
/* check visibile tiles */
mScanBox.scan(v.pos.x, v.pos.y, v.pos.scale, tileZoom, v.plane);
}
}
/* prepare tiles for rendering */
if (compileTileLayers(mDrawTiles.tiles, mDrawTiles.cnt + mProxyTileCnt) > 0) {
mUploadSerial++;
BufferObject.checkBufferUsage(false);
}
setReady(true);
}
public void clearTiles() {
synchronized (tilelock) {
/* Clear all references to MapTiles as all current
* tiles will also be removed from TileManager. */
mDrawTiles.releaseTiles();
mDrawTiles.tiles = new MapTile[1];
mDrawTiles.cnt = 0;
}
}
/**
* compile tile layer data and upload to VBOs
*/
private static int compileTileLayers(MapTile[] tiles, int tileCnt) {
int uploadCnt = 0;
for (int i = 0; i < tileCnt; i++) {
MapTile tile = tiles[i];
if (!tile.isVisible)
continue;
if (tile.state(READY))
continue;
if (tile.state(NEW_DATA)) {
uploadCnt += uploadTileData(tile);
continue;
}
/* load tile that is referenced by this holder */
MapTile proxy = tile.holder;
if (proxy != null && (proxy.state(NEW_DATA) || proxy.state(READY))) {
tile.state = NEW_DATA; // Change independently of proxy state, as long as it isn't READY
//uploadCnt += uploadTileData(proxy); // Should already been done in separate call
uploadCnt += uploadTileData(tile); // Actual tile must be loaded immediately
continue;
}
/* check near relatives than can serve as proxy */
proxy = tile.getProxy(PROXY_PARENT, NEW_DATA);
if (proxy != null) {
uploadCnt += uploadTileData(proxy);
/* don't load child proxies */
continue;
}
for (int c = 0; c < 4; c++) {
proxy = tile.getProxyChild(c, NEW_DATA);
if (proxy != null)
uploadCnt += uploadTileData(proxy);
}
if (uploadCnt >= MAX_TILE_LOAD)
break;
}
return uploadCnt;
}
private static int uploadTileData(MapTile tile) {
tile.setState(READY);
RenderBuckets buckets = tile.getBuckets();
/* tile might only contain label layers */
if (buckets == null)
return 0;
if (!buckets.compile(true)) {
buckets.clear();
return 0;
}
return 1;
}
private final Object tilelock = new Object();
/**
* 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 null;
if (mDrawTiles == null) {
releaseTiles(tileSet);
return null;
}
Integer zoom = null;
int prevSerial = tileSet.serial;
/* ensure tiles keep visible state */
synchronized (tilelock) {
MapTile[] newTiles = mDrawTiles.tiles;
int cnt = mDrawTiles.cnt;
/* ensure same size */
if (tileSet.tiles.length != newTiles.length) {
tileSet.tiles = new MapTile[newTiles.length];
}
/* lock tiles to not be removed from cache */
tileSet.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state(READY))
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) {
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 ? zoom : null;
}
public void releaseTiles(TileSet tileSet) {
tileSet.releaseTiles();
}
/**
* scanline fill class used to check tile visibility
*/
private final ScanBox mScanBox = new ScanBox() {
@Override
protected void setVisible(int y, int x1, int x2) {
MapTile[] tiles = mDrawTiles.tiles;
int proxyOffset = mDrawTiles.cnt;
for (int i = 0; i < proxyOffset; i++) {
MapTile t = tiles[i];
if (t.tileY == y && t.tileX >= x1 && t.tileX < x2)
t.isVisible = true;
}
/* add placeholder tiles to show both sides
* of date line. a little too complicated... */
int xmax = 1 << mZoom;
if (x1 >= 0 && x2 < xmax)
return;
O:
for (int x = x1; x < x2; x++) {
if (x >= 0 && x < xmax)
continue;
int xx = x;
if (x < 0)
xx = xmax + x;
else
xx = x - xmax;
if (xx < 0 || xx >= xmax)
continue;
for (int i = proxyOffset; i < proxyOffset + mProxyTileCnt; i++)
if (tiles[i].tileX == x && tiles[i].tileY == y)
continue O;
MapTile tile = null;
for (int i = 0; i < proxyOffset; i++)
if (tiles[i].tileX == xx && tiles[i].tileY == y) {
tile = tiles[i];
break;
}
if (tile == null)
continue;
if (proxyOffset + mProxyTileCnt >= tiles.length) {
//log.error(" + mNumTileHolder");
break;
}
MapTile holder = new MapTile(null, x, y, (byte) mZoom);
holder.isVisible = true;
holder.holder = tile;
holder.state = tile.state;
tile.isVisible = true;
tiles[proxyOffset + mProxyTileCnt++] = holder;
}
}
};
/**
* @param proxyLevel zoom-level of tile relative to current TileSet
*/
public static long getMinFade(MapTile tile, int proxyLevel) {
long minFade = MapRenderer.frametime - 50;
/* check children for grandparent, parent or current */
if (proxyLevel <= 0) {
for (int c = 0; c < 4; c++) {
MapTile ci = tile.node.child(c);
if (ci == null)
continue;
if (ci.fadeTime > 0 && ci.fadeTime < minFade)
minFade = ci.fadeTime;
/* when drawing the parent of the current level
* we also check if the children of current level
* are visible */
if (proxyLevel >= -1) {
long m = getMinFade(ci, proxyLevel - 1);
if (m < minFade)
minFade = m;
}
}
}
/* check parents for child, current or parent */
TileNode p = tile.node.parent;
for (int i = proxyLevel; i >= -1; i--) {
if (p == null)
break;
if (p.item != null && p.item.fadeTime > 0 && p.item.fadeTime < minFade)
minFade = p.item.fadeTime;
p = p.parent;
}
return minFade;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy