diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/vector/VectorTileLoader.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/vector/VectorTileLoader.java index fa69f946..f4432d40 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/vector/VectorTileLoader.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/vector/VectorTileLoader.java @@ -18,6 +18,7 @@ import org.oscim.backend.Log; 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.core.Tile; @@ -32,6 +33,7 @@ import org.oscim.renderer.elements.TextItem; import org.oscim.theme.IRenderTheme; import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Circle; +import org.oscim.theme.renderinstruction.Extrusion; import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.LineSymbol; import org.oscim.theme.renderinstruction.RenderInstruction; @@ -44,18 +46,7 @@ import org.oscim.tiling.source.ITileDataSink; import org.oscim.tiling.source.ITileDataSource; import org.oscim.tiling.source.ITileDataSource.QueryResult; import org.oscim.utils.LineClipper; -import org.oscim.utils.pool.Inlist; -/** - * @note - * 1. The MapWorkers call MapTileLoader.execute() to load a tile. - * 2. The tile data will be loaded from current MapDatabase - * 3. MapDatabase calls the IMapDataSink functions - * implemented by MapTileLoader for WAY and POI items. - * 4. these callbacks then call RenderTheme to get the matching style. - * 5. RenderTheme calls IRenderCallback functions with style information - * 6. Styled items become added to MapTile.layers... roughly - */ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callback, ITileDataSink { private static final String TAG = VectorTileLoader.class.getName(); @@ -66,47 +57,41 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac public static final byte STROKE_MIN_ZOOM = 12; public static final byte STROKE_MAX_ZOOM = 17; - // replacement for variable value tags that should not be matched by RenderTheme - // FIXME make this general, maybe subclass tags - private static final Tag mTagEmptyName = new Tag(Tag.KEY_NAME, null, false); - private static final Tag mTagEmptyHouseNr = new Tag(Tag.KEY_HOUSE_NUMBER, null, false); - private IRenderTheme renderTheme; private int renderLevels; - // current TileDataSource used by this MapTileLoader + /** current TileDataSource used by this MapTileLoader */ private ITileDataSource mTileDataSource; - // currently processed tile + /** currently processed tile */ private MapTile mTile; - // currently processed MapElement + /** currently processed MapElement */ private MapElement mElement; - // current line layer (will be used for following outline layers) + /** current line layer (will be used for outline layers) */ private LineLayer mCurLineLayer; - private int mDrawingLayer; + /** Current layer for adding elements */ + private int mCurLayer; - private float mStrokeScale = 1.0f; - private float mLatScaleFactor; - private float mGroundResolution; - - private Tag mTagName; - private Tag mTagHouseNr; + /** Line-scale-factor depending on zoom and latitude */ + private float mLineScale = 1.0f; private final LineClipper mClipper; + private final TagSet mFilteredTags; + public void setRenderTheme(IRenderTheme theme) { renderTheme = theme; renderLevels = theme.getLevels(); } - /** - */ public VectorTileLoader(TileManager tileManager) { super(tileManager); + mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE, true); + mFilteredTags = new TagSet(); } @Override @@ -115,31 +100,22 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac } @Override - public boolean executeJob(MapTile mapTile) { + public boolean executeJob(MapTile tile) { if (mTileDataSource == null) return false; - mTile = mapTile; - - if (mTile.layers != null) { - // should be fixed now. - Log.d(TAG, "BUG tile already loaded " + mTile + " " + mTile.getState()); - mTile = null; - return false; - } - - setScaleStrokeWidth(mTile.zoomLevel); - // account for area changes with latitude - double latitude = MercatorProjection.toLatitude(mTile.y); + double lat = MercatorProjection.toLatitude(tile.y); - mLatScaleFactor = 0.4f + 0.6f * ((float) Math.sin(Math.abs(latitude) * (Math.PI / 180))); + mLineScale = (float) Math.pow(STROKE_INCREASE, tile.zoomLevel - STROKE_MIN_ZOOM); + if (mLineScale < 1) + mLineScale = 1; - mGroundResolution = (float) (Math.cos(latitude * (Math.PI / 180)) - * MercatorProjection.EARTH_CIRCUMFERENCE - / ((long) Tile.SIZE << mTile.zoomLevel)); + // scale line width relative to latitude + PI * thumb + mLineScale *= 0.4f + 0.6f * ((float) Math.sin(Math.abs(lat) * (Math.PI / 180))); + mTile = tile; mTile.layers = new ElementLayers(); // query database, which calls renderWay and renderPOI @@ -159,8 +135,6 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac } } - Tag[] mFilterTags = new Tag[1]; - private static int getValidLayer(int layer) { if (layer < 0) { return 0; @@ -171,19 +145,6 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac } } - /** - * Sets the scale stroke factor for the given zoom level. - * - * @param zoomLevel - * the zoom level for which the scale stroke factor should be - * set. - */ - private void setScaleStrokeWidth(byte zoomLevel) { - mStrokeScale = (float) Math.pow(STROKE_INCREASE, zoomLevel - STROKE_MIN_ZOOM); - if (mStrokeScale < 1) - mStrokeScale = 1; - } - public void setTileDataSource(ITileDataSource mapDatabase) { if (mTileDataSource != null) mTileDataSource.destroy(); @@ -191,52 +152,47 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac mTileDataSource = mapDatabase; } - public ITileDataSource getMapDatabase() { - return mTileDataSource; - } + static class TagReplacement { + public TagReplacement(String key) { + this.key = key; + this.tag = new Tag(key, null); + } - private boolean mRenderBuildingModel; + String key; + Tag tag; + } // Replace tags that should only be matched by key in RenderTheme // to avoid caching RenderInstructions for each way of the same type // only with different name. // Maybe this should be done within RenderTheme, also allowing // to set these replacement rules in theme file. - private boolean filterTags(TagSet in) { - mRenderBuildingModel = false; - Tag[] tags = in.tags; + private static final TagReplacement[] mTagReplacement = { + new TagReplacement(Tag.KEY_NAME), + new TagReplacement(Tag.KEY_HOUSE_NUMBER), + new TagReplacement(Tag.KEY_REF), + new TagReplacement(Tag.KEY_HEIGHT), + new TagReplacement(Tag.KEY_MIN_HEIGHT) + }; - for (int i = 0; i < in.numTags; i++) { - String key = tags[i].key; - if (key == Tag.KEY_NAME) { - if (tags[i].value != null) { - mTagName = tags[i]; - tags[i] = mTagEmptyName; - } - } else if (key == Tag.KEY_HOUSE_NUMBER) { - if (tags[i].value != null) { - mTagHouseNr = tags[i]; - tags[i] = mTagEmptyHouseNr; - } - } else if (mTile.zoomLevel > 16) { - // FIXME, allow overlays to intercept - // this, or use a theme option for this - if (key == Tag.KEY_BUILDING) - mRenderBuildingModel = true; - else if (key == Tag.KEY_HEIGHT) { - try { - mElement.height = Integer.parseInt(tags[i].value); - } catch (Exception e) { - } - } - else if (key == Tag.KEY_MIN_HEIGHT) { - try { - mElement.minHeight = Integer.parseInt(tags[i].value); - } catch (Exception e) { - } + private boolean filterTags(TagSet tagSet) { + Tag[] tags = tagSet.tags; + + mFilteredTags.clear(); + + O: for (int i = 0, n = tagSet.numTags; i < n; i++) { + Tag t = tags[i]; + + for (TagReplacement replacement : mTagReplacement) { + if (t.key == replacement.key) { + mFilteredTags.add(replacement.tag); + continue O; } } + + mFilteredTags.add(t); } + return true; } @@ -250,21 +206,22 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac // remove tags that should not be cached in Rendertheme filterTags(element.tags); - // get render instructions - RenderInstruction[] ri = renderTheme.matchElement(element, mTile.zoomLevel); - - if (ri != null) - renderNode(ri); + // 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)) return; - mDrawingLayer = getValidLayer(element.layer) * renderLevels; + mCurLayer = getValidLayer(element.layer) * renderLevels; - RenderInstruction[] ri = renderTheme.matchElement(element, mTile.zoomLevel); - renderWay(ri); + // get and apply render instructions + renderWay(renderTheme.matchElement(element.type, mFilteredTags, mTile.zoomLevel)); + + //boolean closed = element.type == GeometryType.POLY; + //if (debug.debugTheme && ri == null) + // debugUnmatched(closed, element.tags); mCurLineLayer = null; } @@ -272,6 +229,18 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac mElement = null; } + //private void debugUnmatched(boolean closed, TagSet tags) { + // Log.d(TAG, "DBG way not matched: " + closed + " " + // + Arrays.deepToString(tags)); + // + // mTagName = new Tag("name", tags[0].key + ":" + // + tags[0].value, false); + // + // mElement.tags = closed ? debugTagArea : debugTagWay; + // RenderInstruction[] ri = renderTheme.matchElement(mElement, mTile.zoomLevel); + // renderWay(ri); + //} + private void renderWay(RenderInstruction[] ri) { if (ri == null) return; @@ -289,15 +258,13 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac } private void clearState() { - mTagName = null; - mTagHouseNr = null; mCurLineLayer = null; } - // ----------------- RenderThemeCallback ----------------- + /*** RenderThemeCallback ***/ @Override public void renderWay(Line line, int level) { - int numLayer = mDrawingLayer + level; + int numLayer = mCurLayer + level; if (line.stipple == 0) { if (line.outline && mCurLineLayer == null) { @@ -313,11 +280,10 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac if (lineLayer.line == null) { lineLayer.line = line; - float w = line.width * 0.8f; - if (!line.fixed) { - w *= mStrokeScale; - w *= mLatScaleFactor; - } + float w = line.width; + if (!line.fixed) + w *= mLineScale; + lineLayer.width = w; } @@ -326,8 +292,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac return; } - lineLayer.addLine(mElement.points, mElement.index, - mElement.type == GeometryType.POLY); + lineLayer.addLine(mElement); // keep reference for outline layer mCurLineLayer = lineLayer; @@ -342,85 +307,55 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac lineLayer.line = line; float w = line.width; - if (!line.fixed) { - w *= mStrokeScale; - w *= mLatScaleFactor; - } + if (!line.fixed) + w *= mLineScale; + lineLayer.width = w; } - lineLayer.addLine(mElement.points, mElement.index); + lineLayer.addLine(mElement); } } @Override public void renderArea(Area area, int level) { - int numLayer = mDrawingLayer + level; - - if (mRenderBuildingModel) { - //Log.d(TAG, "add buildings: " + mTile + " " + mPriority); - if (mTile.layers.extrusionLayers == null) - mTile.layers.extrusionLayers = new ExtrusionLayer(0, mGroundResolution); - - ((ExtrusionLayer) mTile.layers.extrusionLayers).addBuildings(mElement); - - return; - } + int numLayer = mCurLayer + level; PolygonLayer layer = mTile.layers.getPolygonLayer(numLayer); if (layer == null) return; - //if (layer.area == null) layer.area = area; - layer.addPolygon(mElement.points, mElement.index); } - private String textValueForKey(Text text) { - String value = null; - - if (text.textKey == Tag.KEY_NAME) { - if (mTagName != null) - value = mTagName.value; - } else if (text.textKey == Tag.KEY_HOUSE_NUMBER) { - if (mTagHouseNr != null) - value = mTagHouseNr.value; - } - return value; - } - @Override public void renderAreaText(Text text) { // TODO place somewhere on polygon - - String value = textValueForKey(text); + String value = mElement.tags.getValue(text.textKey); if (value == null) return; - float x = mElement.points[0]; - float y = mElement.points[1]; - - mTile.addLabel(TextItem.pool.get().set(x, y, value, text)); + PointF p = mElement.getPoint(0); + mTile.addLabel(TextItem.pool.get().set(p.x, p.y, value, text)); } @Override public void renderPointText(Text text) { - String value = textValueForKey(text); + String value = mElement.tags.getValue(text.textKey); if (value == null) return; - for (int i = 0, n = mElement.index[0]; i < n; i += 2) { - float x = mElement.points[i]; - float y = mElement.points[i + 1]; - mTile.addLabel(TextItem.pool.get().set(x, y, value, text)); + for (int i = 0, n = mElement.getNumPoints(); i < n; i++) { + PointF p = mElement.getPoint(i); + mTile.addLabel(TextItem.pool.get().set(p.x, p.y, value, text)); } } @Override public void renderWayText(Text text) { - String value = textValueForKey(text); + String value = mElement.tags.getValue(text.textKey); if (value == null) return; @@ -436,8 +371,26 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac } } + Tag TREE_TAG = new Tag("natural", "tree"); + @Override public void renderPointCircle(Circle circle, int level) { + + if (mElement.tags.contains(TREE_TAG)) + + 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, null, true); + + it.x = p.x; + it.y = p.y; + it.tag = mElement.tags.get(TREE_TAG.key); + + mTile.addSymbol(it); + } + } @Override @@ -446,13 +399,13 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac Log.d(TAG, "missing symbol for " + mElement.tags.asString()); return; } - SymbolItem it = SymbolItem.pool.get(); - it.x = mElement.points[0]; - it.y = mElement.points[1]; - it.texRegion = symbol.texture; - it.billboard = true; + for (int i = 0, n = mElement.getNumPoints(); i < n; i++) { + PointF p = mElement.getPoint(i); - mTile.symbols = Inlist.push(mTile.symbols, it); + SymbolItem it = SymbolItem.pool.get(); + it.set(p.x, p.y, symbol.texture, true); + mTile.addSymbol(it); + } } @Override @@ -463,4 +416,30 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac public void renderWaySymbol(LineSymbol symbol) { } + + @Override + public void renderExtrusion(Extrusion 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 = (ExtrusionLayer) mTile.layers.extrusionLayers; + + if (l == null) { + double lat = MercatorProjection.toLatitude(mTile.y); + float groundScale = (float) (Math.cos(lat * (Math.PI / 180)) + * MercatorProjection.EARTH_CIRCUMFERENCE + / ((long) Tile.SIZE << mTile.zoomLevel)); + + mTile.layers.extrusionLayers = l = new ExtrusionLayer(0, groundScale); + } + l.add(mElement, height, minHeight); + } }