Merge branch 'tile_load_hooks'

This commit is contained in:
Hannes Janetzek 2014-03-18 02:50:43 +01:00
commit 31e1b0ec99
24 changed files with 454 additions and 288 deletions

View File

@ -2,7 +2,6 @@ package org.oscim.layers;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.map.Map;
import org.oscim.tiling.source.bitmap.BitmapTileSource;
@ -14,8 +13,8 @@ public class JeoTileLayer extends BitmapTileLayer {
}
@Override
protected TileLoader createLoader(TileManager tm) {
return new TileLoader(tm) {
protected TileLoader createLoader() {
return new TileLoader(this.getManager()) {
@Override
public void cleanup() {

View File

@ -220,7 +220,7 @@ public class PathLayer extends Layer {
}
}
final class Task {
final static class Task {
ElementLayers layer = new ElementLayers();
MapPosition pos = new MapPosition();
}

View File

@ -17,17 +17,18 @@
package org.oscim.layers.tile;
import org.oscim.core.Tile;
import org.oscim.layers.tile.vector.VectorTileLoader;
import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.TextItem;
import org.oscim.utils.pool.Inlist.List;
import org.oscim.utils.pool.Inlist;
import org.oscim.utils.quadtree.Node;
import org.oscim.utils.quadtree.QuadTree;
/**
* Extends Tile class to hold state and data for concurrent use in
* TileManager (Main Thread),
* TileLoader (Worker Thread) and
* TileRenderer (GL Thread).
* Extends Tile class to hold state and data.
*
* Used concurrently in: TileManager (Main Thread), TileLoader (Worker Thread)
* and TileRenderer (GL Thread).
*/
public class MapTile extends Tile {
@ -63,6 +64,12 @@ public class MapTile extends Tile {
public final static byte CANCEL = 1 << 3;
}
public static abstract class TileData extends Inlist<TileData> {
Object id;
protected abstract void dispose();
}
public MapTile(TileNode node, int tileX, int tileY, int zoomLevel) {
super(tileX, tileY, (byte) zoomLevel);
this.x = (double) tileX / (1 << zoomLevel);
@ -80,6 +87,13 @@ public class MapTile extends Tile {
return state;
}
/**
* List of TileData for rendering. ElementLayers is always at first
* position (for VectorTileLayer). TileLoaderHooks may add additional
* data. See e.g. {@link LabelTileLoaderHook}.
*/
public TileData data;
/**
* absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel)
*/
@ -91,32 +105,27 @@ public class MapTile extends Tile {
*/
public float distance;
/**
* FIXME move to VectorMapTile
* Tile data set by TileLoader.
*/
public final List<SymbolItem> symbols = new List<SymbolItem>();
public final List<TextItem> labels = new List<TextItem>();
public ElementLayers layers;
/**
* Tile is in view region. Set by TileRenderer.
*/
public boolean isVisible;
/**
* Used for fade-effects
*/
public long fadeTime;
/**
* Pointer to access relatives in QuadTree
*/
public final TileNode node;
/**
* to avoid drawing a tile twice per frame
* Used to avoid drawing a tile twice per frame
* TODO remove
*/
int lastDraw = 0;
/**
* Pointer to access relatives in {@link QuadTree}
*/
public final TileNode node;
public final static int PROXY_CHILD1 = 1 << 0;
public final static int PROXY_CHILD2 = 1 << 1;
public final static int PROXY_CHILD3 = 1 << 2;
@ -208,26 +217,53 @@ public class MapTile extends Tile {
}
/**
* Test whether it is save to access a proxy item through
* this.rel.*
* Test whether it is save to access a proxy item
* through this.node.*
*/
public boolean hasProxy(int proxy) {
return (proxies & proxy) != 0;
}
/**
* CAUTION: This function may only be called by {@link TileManager}
* CAUTION: This function may only be called
* by {@link TileManager}
*/
protected void clear() {
if (layers != null) {
layers.clear();
layers = null;
while (data != null) {
data.dispose();
data = data.next;
}
TextItem.pool.releaseAll(labels.clear());
SymbolItem.pool.releaseAll(symbols.clear());
// still needed?
state = State.NONE;
}
/**
* Get the default ElementLayers which are added
* by {@link VectorTileLoader}
*/
public ElementLayers getLayers() {
if (!(data instanceof ElementLayers))
return null;
return (ElementLayers) data;
}
public TileData getData(Object id) {
for (TileData d = data; d != null; d = d.next)
if (d.id == id)
return d;
return null;
}
public void addData(Object id, TileData td) {
// keeping ElementLayers at position 0!
td.id = id;
if (data != null) {
td.next = data.next;
data.next = td;
} else {
data = td;
}
}
}

View File

@ -45,7 +45,7 @@ public abstract class TileLayer extends Layer implements UpdateListener {
}
abstract protected TileLoader createLoader(TileManager tm);
abstract protected TileLoader createLoader();
public TileRenderer tileRenderer() {
return (TileRenderer) mRenderer;
@ -55,7 +55,7 @@ public abstract class TileLayer extends Layer implements UpdateListener {
mTileLoader = new TileLoader[numLoaders];
for (int i = 0; i < numLoaders; i++) {
mTileLoader[i] = createLoader(mTileManager);
mTileLoader[i] = createLoader();
mTileLoader[i].start();
}
}
@ -111,4 +111,8 @@ public abstract class TileLayer extends Layer implements UpdateListener {
for (TileLoader loader : mTileLoader)
loader.proceed();
}
public TileManager getManager() {
return mTileManager;
}
}

View File

@ -26,6 +26,7 @@ import org.oscim.renderer.ElementRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.utils.ScanBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -186,24 +187,25 @@ public abstract class TileRenderer extends LayerRenderer {
private static int uploadTileData(MapTile tile) {
tile.state = READY;
ElementLayers layers = tile.getLayers();
/* tile might contain extrusion or label layers */
if (tile.layers == null)
if (layers == null)
return 1;
int newSize = tile.layers.getSize();
int newSize = layers.getSize();
if (newSize <= 0)
return 1;
if (tile.layers.vbo == null)
tile.layers.vbo = BufferObject.get(GL20.GL_ARRAY_BUFFER, newSize);
if (layers.vbo == null)
layers.vbo = BufferObject.get(GL20.GL_ARRAY_BUFFER, newSize);
if (!ElementRenderer.uploadLayers(tile.layers, newSize, true)) {
if (!ElementRenderer.uploadLayers(layers, newSize, true)) {
log.error("{} uploadTileData failed!", tile);
tile.layers.vbo = BufferObject.release(tile.layers.vbo);
tile.layers.clear();
tile.layers = null;
layers.vbo = BufferObject.release(layers.vbo);
layers.clear();
/* throw Exception? */
//FIXME tile.layers = null;
return 0;
}

View File

@ -16,6 +16,7 @@ import org.oscim.renderer.GLMatrix;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.elements.BitmapLayer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.LineLayer;
import org.oscim.renderer.elements.LineTexLayer;
import org.oscim.renderer.elements.MeshLayer;
@ -113,11 +114,13 @@ public class VectorTileRenderer extends TileRenderer {
/* use holder proxy when it is set */
MapTile t = tile.holder == null ? tile : tile.holder;
if (t.layers == null || t.layers.vbo == null)
ElementLayers layers = t.getLayers();
if (layers == null || layers.vbo == null)
//throw new IllegalStateException(t + "no data " + (t.layers == null));
return;
t.layers.vbo.bind();
layers.vbo.bind();
MapPosition pos = v.pos;
/* place tile relative to map position */
int z = tile.zoomLevel;
@ -134,7 +137,8 @@ public class VectorTileRenderer extends TileRenderer {
boolean clipped = false;
int mode = mClipMode;
RenderElement l = t.layers.getBaseLayers();
RenderElement l = layers.getBaseLayers();
while (l != null) {
if (l.type == POLYGON) {
@ -148,11 +152,11 @@ public class VectorTileRenderer extends TileRenderer {
clipped = true;
}
if (l.type == LINE) {
l = LineLayer.Renderer.draw(l, v, scale, t.layers);
l = LineLayer.Renderer.draw(l, v, scale, layers);
continue;
}
if (l.type == TEXLINE) {
l = LineTexLayer.Renderer.draw(l, v, div, t.layers);
l = LineTexLayer.Renderer.draw(l, v, div, layers);
continue;
}
if (l.type == MESH) {
@ -163,7 +167,7 @@ public class VectorTileRenderer extends TileRenderer {
l = l.next;
}
l = t.layers.getTextureLayers();
l = layers.getTextureLayers();
while (l != null) {
if (!clipped) {
PolygonLayer.Renderer.draw(null, v, div, true, mode);

View File

@ -99,7 +99,7 @@ public class BitmapTileLayer extends TileLayer {
}
@Override
protected TileLoader createLoader(TileManager tm) {
return new BitmapTileLoader(tm, mTileSource);
protected TileLoader createLoader() {
return new BitmapTileLoader(this, mTileSource);
}
}

View File

@ -23,8 +23,8 @@ import java.util.concurrent.CancellationException;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.renderer.elements.BitmapLayer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.tiling.ITileDataSource;
@ -38,8 +38,8 @@ public class BitmapTileLoader extends TileLoader {
private final ITileDataSource mTileDataSource;
public BitmapTileLoader(TileManager tileManager, TileSource tileSource) {
super(tileManager);
public BitmapTileLoader(TileLayer tileLayer, TileSource tileSource) {
super(tileLayer.getManager());
mTileDataSource = tileSource.getDataSource();
}
@ -64,8 +64,9 @@ public class BitmapTileLoader extends TileLoader {
BitmapLayer l = new BitmapLayer(false);
l.setBitmap(bitmap, Tile.SIZE, Tile.SIZE);
mTile.layers = new ElementLayers();
mTile.layers.setTextureLayers(l);
ElementLayers layers = new ElementLayers();
layers.setTextureLayers(l);
mTile.data = layers;
}
@Override

View File

@ -42,13 +42,14 @@ public class TestTileLayer extends TileLayer {
}
@Override
protected TestTileLoader createLoader(TileManager tm) {
return new TestTileLoader(tm);
protected TestTileLoader createLoader() {
return new TestTileLoader(this);
}
static class TestTileLoader extends TileLoader {
public TestTileLoader(TileManager tileManager) {
super(tileManager);
public TestTileLoader(TileLayer tileLayer) {
super(tileLayer.getManager());
}
GeometryBuffer mGeom = new GeometryBuffer(128, 16);
@ -57,9 +58,10 @@ public class TestTileLayer extends TileLayer {
@Override
public boolean loadTile(MapTile tile) {
log.debug("load tile " + tile);
tile.layers = new ElementLayers();
ElementLayers layers = new ElementLayers();
tile.data = layers;
LineLayer ll = tile.layers.getLineLayer(0);
LineLayer ll = layers.getLineLayer(0);
ll.line = mLineStyle;
ll.scale = 2;

View File

@ -16,19 +16,30 @@
*/
package org.oscim.layers.tile.vector;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
import org.oscim.map.Map;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.utils.FastMath;
public class BuildingLayer extends Layer {
public class BuildingLayer extends Layer implements TileLoaderThemeHook {
//static final Logger log = LoggerFactory.getLogger(BuildingOverlay.class);
final ExtrusionRenderer mExtLayer;
public BuildingLayer(Map map, VectorTileLayer tileLayer) {
super(map);
tileLayer.addHook(this);
mExtLayer = new ExtrusionRenderer(tileLayer.tileRenderer()) {
private long mStartTime;
@ -79,6 +90,45 @@ public class BuildingLayer extends Layer {
private final static int MIN_ZOOM = 17;
@Override
public boolean render(MapTile tile, ElementLayers layers, MapElement element,
RenderStyle style, int level) {
if (!(style instanceof ExtrusionStyle))
return false;
ExtrusionStyle extrusion = (ExtrusionStyle) style;
int height = 0;
int minHeight = 0;
String v = element.tags.getValue(Tag.KEY_HEIGHT);
if (v != null)
height = Integer.parseInt(v);
v = element.tags.getValue(Tag.KEY_MIN_HEIGHT);
if (v != null)
minHeight = Integer.parseInt(v);
ExtrusionLayer l = layers.getExtrusionLayers();
if (l == null) {
double lat = MercatorProjection.toLatitude(tile.y);
float groundScale = (float) MercatorProjection
.groundResolution(lat, 1 << tile.zoomLevel);
l = new ExtrusionLayer(0, groundScale, extrusion.colors);
layers.setExtrusionLayers(l);
}
/* 12m default */
if (height == 0)
height = 12 * 100;
l.add(element, height, minHeight);
return true;
}
//@Override
//public boolean onTouchEvent(MotionEvent e) {
// int action = e.getAction() & MotionEvent.ACTION_MASK;

View File

@ -3,7 +3,6 @@ package org.oscim.layers.tile.vector;
import org.oscim.core.Tag;
import org.oscim.core.TagSet;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.map.Map;
public class OsmTileLayer extends VectorTileLayer {
@ -17,14 +16,16 @@ public class OsmTileLayer extends VectorTileLayer {
}
@Override
protected TileLoader createLoader(TileManager tm) {
return new OsmTileLoader(tm);
protected TileLoader createLoader() {
return new OsmTileLoader(this);
}
static class OsmTileLoader extends VectorTileLoader {
private final TagSet mFilteredTags;
public OsmTileLoader(TileManager tileManager) {
super(tileManager);
public OsmTileLoader(VectorTileLayer tileLayer) {
super(tileLayer);
mFilteredTags = new TagSet();
}
/* Replace tags that should only be matched by key in RenderTheme
@ -40,7 +41,7 @@ public class OsmTileLayer extends VectorTileLayer {
new TagReplacement(Tag.KEY_MIN_HEIGHT)
};
protected boolean filterTags(TagSet tagSet) {
protected TagSet filterTags(TagSet tagSet) {
Tag[] tags = tagSet.tags;
mFilteredTags.clear();
@ -58,7 +59,7 @@ public class OsmTileLayer extends VectorTileLayer {
mFilteredTags.add(t);
}
return true;
return mFilteredTags;
}
}
}

View File

@ -16,12 +16,16 @@
*/
package org.oscim.layers.tile.vector;
import org.oscim.core.MapElement;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.VectorTileRenderer;
import org.oscim.map.Map;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.tiling.TileSource;
import org.oscim.tiling.TileSource.OpenResult;
import org.slf4j.Logger;
@ -58,8 +62,8 @@ public class VectorTileLayer extends TileLayer {
}
@Override
protected TileLoader createLoader(TileManager tm) {
return new VectorTileLoader(tm);
protected TileLoader createLoader() {
return new VectorTileLoader(this);
}
/**
@ -98,21 +102,58 @@ public class VectorTileLayer extends TileLayer {
* Set {@link IRenderTheme} used by {@link TileLoader}
*/
public void setRenderTheme(IRenderTheme theme) {
// wait for loaders to finish all current jobs to
// not change theme instance hold by loader instance
// while running
/* wait for loaders to finish all current jobs to
* not change theme instance hold by loader instance
* while running */
pauseLoaders(true);
mTileManager.clearJobs();
for (TileLoader l : mTileLoader)
((VectorTileLoader) l).setRenderTheme(theme);
mTheme = theme;
// for (TileLoader l : mTileLoader)
// ((VectorTileLoader) l).setRenderTheme(theme);
tileRenderer().setOverdrawColor(theme.getMapBackground());
resumeLoaders();
}
public TileManager getManager() {
return mTileManager;
private IRenderTheme mTheme;
public IRenderTheme getTheme() {
return mTheme;
}
public interface TileLoaderProcessHook {
public boolean process(MapTile tile, ElementLayers layers, MapElement element);
}
public interface TileLoaderThemeHook {
public boolean render(MapTile tile, ElementLayers layers,
MapElement element, RenderStyle style, int level);
}
private TileLoaderProcessHook[] mLoaderProcessHooks = new TileLoaderProcessHook[0];
private TileLoaderThemeHook[] mLoaderThemeHooks = new TileLoaderThemeHook[0];
public TileLoaderProcessHook[] loaderProcessHooks() {
return mLoaderProcessHooks;
}
public TileLoaderThemeHook[] loaderThemeHooks() {
return mLoaderThemeHooks;
}
public void addHook(TileLoaderProcessHook h) {
TileLoaderProcessHook[] tmp = mLoaderProcessHooks;
mLoaderProcessHooks = new TileLoaderProcessHook[tmp.length + 1];
System.arraycopy(tmp, 0, mLoaderProcessHooks, 0, tmp.length);
mLoaderProcessHooks[tmp.length] = h;
}
public void addHook(TileLoaderThemeHook h) {
TileLoaderThemeHook[] tmp = mLoaderThemeHooks;
mLoaderThemeHooks = new TileLoaderThemeHook[tmp.length + 1];
System.arraycopy(tmp, 0, mLoaderThemeHooks, 0, tmp.length);
mLoaderThemeHooks[tmp.length] = h;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012, 2013 Hannes Janetzek
* Copyright 2012-2014 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -20,24 +20,20 @@ import static org.oscim.layers.tile.MapTile.State.CANCEL;
import java.util.concurrent.CancellationException;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.core.PointF;
import org.oscim.core.Tag;
import org.oscim.core.TagSet;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderProcessHook;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.renderer.elements.LineLayer;
import org.oscim.renderer.elements.LineTexLayer;
import org.oscim.renderer.elements.MeshLayer;
import org.oscim.renderer.elements.PolygonLayer;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.TextItem;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.RenderTheme;
import org.oscim.theme.styles.AreaStyle;
@ -62,7 +58,6 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
public static final byte STROKE_MAX_ZOOM = 17;
protected IRenderTheme renderTheme;
protected int renderLevels;
/** current TileDataSource used by this MapTileLoader */
protected ITileDataSource mTileDataSource;
@ -79,17 +74,13 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
/** Line-scale-factor depending on zoom and latitude */
protected float mLineScale = 1.0f;
protected final TagSet mFilteredTags;
protected ElementLayers mLayers;
public void setRenderTheme(IRenderTheme theme) {
renderTheme = theme;
renderLevels = theme.getLevels();
}
private final VectorTileLayer mTileLayer;
public VectorTileLoader(TileManager tileManager) {
super(tileManager);
mFilteredTags = new TagSet();
public VectorTileLoader(VectorTileLayer tileLayer) {
super(tileLayer.getManager());
mTileLayer = tileLayer;
}
@Override
@ -105,12 +96,14 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
log.error("no tile source is set");
return false;
}
renderTheme = mTileLayer.getTheme();
if (renderTheme == null) {
log.error("no theme is set");
return false;
}
//mTileLayer.getLoaderHooks();
/* account for area changes with latitude */
double lat = MercatorProjection.toLatitude(tile.y);
mLineScale = (float) Math.pow(STROKE_INCREASE, tile.zoomLevel - STROKE_MIN_ZOOM);
@ -119,8 +112,8 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
/* scale line width relative to latitude + PI * thumb */
mLineScale *= 0.4f + 0.6f * ((float) Math.sin(Math.abs(lat) * (Math.PI / 180)));
tile.layers = new ElementLayers();
mLayers = new ElementLayers();
tile.data = mLayers;
try {
/* query data source, which calls process() callback */
@ -151,11 +144,11 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
}
}
public void setDataSource(ITileDataSource mapDatabase) {
public void setDataSource(ITileDataSource dataSource) {
if (mTileDataSource != null)
mTileDataSource.destroy();
mTileDataSource = mapDatabase;
mTileDataSource = dataSource;
}
static class TagReplacement {
@ -171,46 +164,36 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
/**
* Override this method to change tags that should be passed
* to {@link RenderTheme} matching.
* E.g. to replace tags that should not be cached in Rendertheme
*/
protected boolean filterTags(TagSet tagSet) {
mFilteredTags.clear();
for (int i = 0; i < tagSet.numTags; i++)
mFilteredTags.add(tagSet.tags[i]);
return true;
protected TagSet filterTags(TagSet tagSet) {
return tagSet;
}
@Override
public void process(MapElement element) {
clearState();
if (isCanceled() || mTile.state(CANCEL))
throw new CancellationException();
mElement = element;
if (element.type == GeometryType.POINT) {
// remove tags that should not be cached in Rendertheme
filterTags(element.tags);
// get and apply render instructions
renderNode(renderTheme.matchElement(element.type, mFilteredTags, mTile.zoomLevel));
} else {
// replace tags that should not be cached in Rendertheme (e.g. name)
if (!filterTags(element.tags))
for (TileLoaderProcessHook h : mTileLayer.loaderProcessHooks())
if (h.process(mTile, mLayers, element))
return;
mCurLayer = getValidLayer(element.layer) * renderLevels;
TagSet tags = filterTags(element.tags);
if (tags == null)
return;
// get and apply render instructions
renderWay(renderTheme.matchElement(element.type, mFilteredTags, mTile.zoomLevel));
mElement = element;
//boolean closed = element.type == GeometryType.POLY;
mCurLineLayer = null;
/* get and apply render instructions */
if (element.type == GeometryType.POINT) {
renderNode(renderTheme.matchElement(element.type, tags, mTile.zoomLevel));
} else {
mCurLayer = getValidLayer(element.layer) * renderTheme.getLevels();
renderWay(renderTheme.matchElement(element.type, tags, mTile.zoomLevel));
}
mElement = null;
clearState();
}
//protected void debugUnmatched(boolean closed, TagSet tags) {
@ -243,6 +226,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
protected void clearState() {
mCurLineLayer = null;
mElement = null;
}
/*** RenderThemeCallback ***/
@ -257,7 +241,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
return;
}
LineLayer ll = mTile.layers.getLineLayer(numLayer);
LineLayer ll = mLayers.getLineLayer(numLayer);
if (ll.line == null) {
ll.line = line;
@ -271,11 +255,11 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
ll.addLine(mElement);
// NB: keep reference for outline layer(s)
/* keep reference for outline layer(s) */
mCurLineLayer = ll;
} else {
LineTexLayer ll = mTile.layers.getLineTexLayer(numLayer);
LineTexLayer ll = mLayers.getLineTexLayer(numLayer);
if (ll.line == null) {
ll.line = line;
@ -291,129 +275,46 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
}
}
// slower to load (requires tesselation) and uses
// more memory but should be faster to render
/* slower to load (requires tesselation) and uses
* more memory but should be faster to render */
protected final static boolean USE_MESH_POLY = false;
@Override
public void renderArea(AreaStyle area, int level) {
int numLayer = mCurLayer + level;
if (USE_MESH_POLY) {
MeshLayer l = mTile.layers.getMeshLayer(numLayer);
MeshLayer l = mLayers.getMeshLayer(numLayer);
l.area = area;
l.addMesh(mElement);
} else {
PolygonLayer l = mTile.layers.getPolygonLayer(numLayer);
PolygonLayer l = mLayers.getPolygonLayer(numLayer);
l.area = area;
l.addPolygon(mElement.points, mElement.index);
}
}
@Override
public void renderAreaText(TextStyle text) {
// TODO place somewhere on polygon
String value = mElement.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return;
float x = 0;
float y = 0;
int n = mElement.index[0];
for (int i = 0; i < n;) {
x += mElement.points[i++];
y += mElement.points[i++];
}
x /= (n / 2);
y /= (n / 2);
mTile.labels.push(TextItem.pool.get().set(x, y, value, text));
}
@Override
public void renderPointText(TextStyle text) {
String value = mElement.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return;
for (int i = 0, n = mElement.getNumPoints(); i < n; i++) {
PointF p = mElement.getPoint(i);
mTile.labels.push(TextItem.pool.get().set(p.x, p.y, value, text));
}
}
@Override
public void renderWayText(TextStyle text) {
String value = mElement.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return;
int offset = 0;
for (int i = 0, n = mElement.index.length; i < n; i++) {
int length = mElement.index[i];
if (length < 4)
public void renderSymbol(SymbolStyle symbol) {
for (TileLoaderThemeHook h : mTileLayer.loaderThemeHooks())
if (h.render(mTile, mLayers, mElement, symbol, 0))
break;
WayDecorator.renderText(null, mElement.points, value, text,
offset, length, mTile);
offset += length;
}
}
@Override
public void renderPointCircle(CircleStyle circle, int level) {
}
@Override
public void renderPointSymbol(SymbolStyle symbol) {
if (symbol.texture == null)
return;
for (int i = 0, n = mElement.getNumPoints(); i < n; i++) {
PointF p = mElement.getPoint(i);
SymbolItem it = SymbolItem.pool.get();
it.set(p.x, p.y, symbol.texture, true);
mTile.symbols.push(it);
}
}
@Override
public void renderAreaSymbol(SymbolStyle symbol) {
}
@Override
public void renderExtrusion(ExtrusionStyle extrusion, int level) {
int height = 0;
int minHeight = 0;
String v = mElement.tags.getValue(Tag.KEY_HEIGHT);
if (v != null)
height = Integer.parseInt(v);
v = mElement.tags.getValue(Tag.KEY_MIN_HEIGHT);
if (v != null)
minHeight = Integer.parseInt(v);
ExtrusionLayer l = mTile.layers.getExtrusionLayers();
if (l == null) {
double lat = MercatorProjection.toLatitude(mTile.y);
float groundScale = (float) MercatorProjection
.groundResolution(lat, 1 << mTile.zoomLevel);
l = new ExtrusionLayer(0, groundScale, extrusion.colors);
mTile.layers.setExtrusionLayers(l);
}
/* 12m default */
if (height == 0)
height = 12 * 100;
l.add(mElement, height, minHeight);
for (TileLoaderThemeHook h : mTileLayer.loaderThemeHooks())
if (h.render(mTile, mLayers, mElement, extrusion, level))
break;
}
@Override
public void setTileImage(Bitmap bitmap) {
public void renderCircle(CircleStyle circle, int level) {
}
@Override
public void renderText(TextStyle text) {
for (TileLoaderThemeHook h : mTileLayer.loaderThemeHooks())
if (h.render(mTile, mLayers, mElement, text, 0))
break;
}
}

View File

@ -31,6 +31,8 @@ public class LabelLayer extends Layer implements Map.UpdateListener, TileManager
static final Logger log = LoggerFactory.getLogger(LabelLayer.class);
public final static String LABEL_DATA = LabelLayer.class.getName();
private final static long MAX_RELABEL_DELAY = 100;
private final LabelPlacement mLabelPlacer;
@ -39,6 +41,7 @@ public class LabelLayer extends Layer implements Map.UpdateListener, TileManager
public LabelLayer(Map map, VectorTileLayer l) {
super(map);
l.getManager().events.bind(this);
l.addHook(new LabelTileLoaderHook());
mLabelPlacer = new LabelPlacement(map, l.tileRenderer());
mWorker = new Worker(map);

View File

@ -18,6 +18,10 @@ import org.oscim.utils.geom.OBB2D;
public class LabelPlacement {
static final boolean dbg = false;
public final static LabelTileData getLabels(MapTile tile) {
return (LabelTileData) tile.getData(LabelLayer.LABEL_DATA);
}
private final static float MIN_CAPTION_DIST = 5;
private final static float MIN_WAY_DIST = 3;
@ -176,7 +180,11 @@ public class LabelPlacement {
private Label addWayLabels(MapTile t, Label l, float dx, float dy,
double scale) {
for (TextItem ti : t.labels) {
LabelTileData ld = getLabels(t);
if (ld == null)
return l;
for (TextItem ti : ld.labels) {
if (ti.text.caption)
continue;
@ -229,7 +237,11 @@ public class LabelPlacement {
private Label addNodeLabels(MapTile t, Label l, float dx, float dy,
double scale, float cos, float sin) {
O: for (TextItem ti : t.labels) {
LabelTileData ld = getLabels(t);
if (ld == null)
return l;
O: for (TextItem ti : ld.labels) {
if (!ti.text.caption)
continue;
@ -447,7 +459,11 @@ public class LabelPlacement {
float dy = (float) (t.tileY * Tile.SIZE - tileY);
dx = flipLongitude(dx, maxx);
for (SymbolItem ti : t.symbols) {
LabelTileData ld = getLabels(t);
if (ld == null)
continue;
for (SymbolItem ti : ld.symbols) {
if (ti.texRegion == null)
continue;

View File

@ -0,0 +1,16 @@
package org.oscim.layers.tile.vector.labeling;
import org.oscim.layers.tile.MapTile.TileData;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.TextItem;
public class LabelTileData extends TileData {
public final List<SymbolItem> symbols = new List<SymbolItem>();
public final List<TextItem> labels = new List<TextItem>();
@Override
protected void dispose() {
TextItem.pool.releaseAll(labels.clear());
SymbolItem.pool.releaseAll(symbols.clear());
}
}

View File

@ -0,0 +1,106 @@
package org.oscim.layers.tile.vector.labeling;
import static org.oscim.core.GeometryBuffer.GeometryType.LINE;
import static org.oscim.core.GeometryBuffer.GeometryType.POINT;
import static org.oscim.core.GeometryBuffer.GeometryType.POLY;
import static org.oscim.layers.tile.vector.labeling.LabelLayer.LABEL_DATA;
import org.oscim.core.MapElement;
import org.oscim.core.PointF;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.TextItem;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.theme.styles.SymbolStyle;
import org.oscim.theme.styles.TextStyle;
public class LabelTileLoaderHook implements TileLoaderThemeHook {
//public final static LabelTileData EMPTY = new LabelTileData();
private LabelTileData get(MapTile tile) {
// FIXME could be 'this'..
LabelTileData ld = (LabelTileData) tile.getData(LABEL_DATA);
if (ld == null) {
ld = new LabelTileData();
tile.addData(LABEL_DATA, ld);
}
return ld;
}
@Override
public boolean render(MapTile tile, ElementLayers layers, MapElement element,
RenderStyle style, int level) {
if (style instanceof TextStyle) {
LabelTileData ld = get(tile);
TextStyle text = (TextStyle) style;
if (element.type == LINE) {
String value = element.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return false;
int offset = 0;
for (int i = 0, n = element.index.length; i < n; i++) {
int length = element.index[i];
if (length < 4)
break;
WayDecorator.renderText(null, element.points, value, text,
offset, length, ld);
offset += length;
}
}
else if (element.type == POLY) {
// TODO place somewhere on polygon
String value = element.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return false;
float x = 0;
float y = 0;
int n = element.index[0];
for (int i = 0; i < n;) {
x += element.points[i++];
y += element.points[i++];
}
x /= (n / 2);
y /= (n / 2);
ld.labels.push(TextItem.pool.get().set(x, y, value, text));
}
else if (element.type == POINT) {
String value = element.tags.getValue(text.textKey);
if (value == null || value.length() == 0)
return false;
for (int i = 0, n = element.getNumPoints(); i < n; i++) {
PointF p = element.getPoint(i);
ld.labels.push(TextItem.pool.get().set(p.x, p.y, value, text));
}
}
}
else if ((element.type == POINT) && (style instanceof SymbolStyle)) {
SymbolStyle symbol = (SymbolStyle) style;
if (symbol.texture == null)
return false;
LabelTileData ld = get(tile);
for (int i = 0, n = element.getNumPoints(); i < n; i++) {
PointF p = element.getPoint(i);
SymbolItem it = SymbolItem.pool.get();
it.set(p.x, p.y, symbol.texture, true);
ld.symbols.push(it);
}
}
return false;
}
}

View File

@ -15,10 +15,9 @@
* 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.vector;
package org.oscim.layers.tile.vector.labeling;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.renderer.elements.TextItem;
import org.oscim.theme.styles.TextStyle;
import org.oscim.utils.geom.GeometryUtils;
@ -27,7 +26,7 @@ import org.oscim.utils.geom.LineClipper;
public final class WayDecorator {
public static void renderText(LineClipper clipper, float[] coordinates, String string,
TextStyle text, int pos, int len, MapTile tile) {
TextStyle text, int pos, int len, LabelTileData ld) {
//TextItem items = textItems;
TextItem t = null;
@ -216,7 +215,7 @@ public final class WayDecorator {
t.length = (short) segmentLength;
t.edges = edge;
tile.labels.push(t);
ld.labels.push(t);
i = last;
}

View File

@ -25,6 +25,7 @@ import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.layers.tile.TileSet;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -169,10 +170,11 @@ public class ExtrusionRenderer extends LayerRenderer {
}
private static ExtrusionLayer getLayer(MapTile t) {
if (t.layers == null || !t.state(READY | NEW_DATA))
ElementLayers layers = t.getLayers();
if (layers == null || !t.state(READY | NEW_DATA))
return null;
return t.layers.getExtrusionLayers();
return layers.getExtrusionLayers();
}
private final boolean debug = false;
@ -201,7 +203,7 @@ public class ExtrusionRenderer extends LayerRenderer {
GLState.test(false, false);
for (int i = 0; i < mTileCnt; i++) {
ExtrusionLayer el = tiles[i].layers.getExtrusionLayers();
ExtrusionLayer el = tiles[i].getLayers().getExtrusionLayers();
setMatrix(v, tiles[i], 0);
v.mvp.setAsUniform(uExtMatrix);
@ -252,7 +254,7 @@ public class ExtrusionRenderer extends LayerRenderer {
// draw to depth buffer
for (int i = 0; i < mTileCnt; i++) {
MapTile t = tiles[i];
ExtrusionLayer el = t.layers.getExtrusionLayers();
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
int d = MapRenderer.depthOffset(t) * 10;
setMatrix(v, t, d);
v.mvp.setAsUniform(uExtMatrix);
@ -278,7 +280,7 @@ public class ExtrusionRenderer extends LayerRenderer {
for (int i = 0; i < mTileCnt; i++) {
MapTile t = tiles[i];
ExtrusionLayer el = t.layers.getExtrusionLayers();
ExtrusionLayer el = t.getLayers().getExtrusionLayers();
if (el.colors == null) {
currentColor = mColor;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012, 2013 Hannes Janetzek
* Copyright 2012-2014 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -24,6 +24,7 @@ import static org.oscim.renderer.elements.RenderElement.TEXLINE;
import java.nio.ShortBuffer;
import org.oscim.backend.GL20;
import org.oscim.layers.tile.MapTile.TileData;
import org.oscim.renderer.BufferObject;
import org.oscim.theme.styles.AreaStyle;
import org.oscim.theme.styles.LineStyle;
@ -35,7 +36,8 @@ import org.slf4j.LoggerFactory;
* MapTile. It can be used for other purposes as well but some optimizations
* (and limitations) probably wont make sense in different contexts.
*/
public class ElementLayers {
public class ElementLayers extends TileData {
static final Logger log = LoggerFactory.getLogger(ElementLayers.class);
public static void initRenderer(GL20 gl) {
@ -57,6 +59,10 @@ public class ElementLayers {
/** Text- and SymbolLayer */
private RenderElement textureLayers;
/**
* FIXME this is somewhat out-of-place, as it is not
* compiled with the other layers
*/
private RenderElement extrusionLayers;
/**
@ -386,4 +392,9 @@ public class ElementLayers {
vbo = BufferObject.release(vbo);
}
@Override
protected void dispose() {
clear();
}
}

View File

@ -85,22 +85,6 @@ public interface IRenderTheme {
*/
void renderExtrusion(ExtrusionStyle extrusion, int level);
/**
* Renders an area symbol with the given bitmap.
*
* @param symbol
* the symbol to be rendered.
*/
void renderAreaSymbol(SymbolStyle symbol);
/**
* Renders an area caption with the given text.
*
* @param text
* the text to be rendered.
*/
void renderAreaText(TextStyle text);
/**
* Renders a point of interest circle with the given parameters.
*
@ -109,7 +93,7 @@ public interface IRenderTheme {
* @param level
* the drawing level on which the circle should be rendered.
*/
void renderPointCircle(CircleStyle circle, int level);
void renderCircle(CircleStyle circle, int level);
/**
* Renders a point of interest symbol with the given bitmap.
@ -117,15 +101,7 @@ public interface IRenderTheme {
* @param symbol
* the symbol to be rendered.
*/
void renderPointSymbol(SymbolStyle symbol);
/**
* Renders a point of interest caption with the given text.
*
* @param text
* the text to be rendered.
*/
void renderPointText(TextStyle text);
void renderSymbol(SymbolStyle symbol);
/**
* Renders a way with the given parameters.
@ -140,7 +116,7 @@ public interface IRenderTheme {
*
* @param text
*/
void renderWayText(TextStyle text);
void renderText(TextStyle text);
}

View File

@ -49,6 +49,6 @@ public final class CircleStyle extends RenderStyle {
@Override
public void renderNode(Callback renderCallback) {
renderCallback.renderPointCircle(this, this.level);
renderCallback.renderCircle(this, this.level);
}
}

View File

@ -36,12 +36,12 @@ public final class SymbolStyle extends RenderStyle {
}
@Override
public void renderNode(Callback renderCallback) {
renderCallback.renderPointSymbol(this);
public void renderNode(Callback cb) {
cb.renderSymbol(this);
}
@Override
public void renderWay(Callback renderCallback) {
renderCallback.renderAreaSymbol(this);
public void renderWay(Callback cb) {
cb.renderSymbol(this);
}
}

View File

@ -182,17 +182,13 @@ public final class TextStyle extends RenderStyle {
public final TextureRegion texture;
@Override
public void renderNode(Callback renderCallback) {
if (caption)
renderCallback.renderPointText(this);
public void renderNode(Callback cb) {
cb.renderText(this);
}
@Override
public void renderWay(Callback renderCallback) {
if (caption)
renderCallback.renderAreaText(this);
else
renderCallback.renderWayText(this);
public void renderWay(Callback cb) {
cb.renderText(this);
}
@Override