All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.oscim.tiling.source.mvt.TileDecoder Maven / Gradle / Ivy
/*
* Copyright 2013 Hannes Janetzek
* Copyright 2016-2017 devemux86
* Copyright 2017 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.tiling.source.mvt;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.Tag;
import org.oscim.core.Tile;
import org.oscim.tiling.ITileDataSink;
import org.oscim.tiling.source.PbfDecoder;
import org.oscim.utils.FastMath;
import org.oscim.utils.pool.Inlist;
import org.oscim.utils.pool.Pool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
public class TileDecoder extends PbfDecoder {
private static final Logger log = LoggerFactory.getLogger(TileDecoder.class);
private static final int TAG_TILE_LAYERS = 3;
private static final int TAG_LAYER_VERSION = 15;
private static final int TAG_LAYER_NAME = 1;
private static final int TAG_LAYER_FEATURES = 2;
private static final int TAG_LAYER_KEYS = 3;
private static final int TAG_LAYER_VALUES = 4;
private static final int TAG_LAYER_EXTENT = 5;
private static final int TAG_FEATURE_ID = 1;
private static final int TAG_FEATURE_TAGS = 2;
private static final int TAG_FEATURE_TYPE = 3;
private static final int TAG_FEATURE_GEOMETRY = 4;
private static final int TAG_VALUE_STRING = 1;
private static final int TAG_VALUE_FLOAT = 2;
private static final int TAG_VALUE_DOUBLE = 3;
private static final int TAG_VALUE_LONG = 4;
private static final int TAG_VALUE_UINT = 5;
private static final int TAG_VALUE_SINT = 6;
private static final int TAG_VALUE_BOOL = 7;
private static final int TAG_GEOM_UNKNOWN = 0;
private static final int TAG_GEOM_POINT = 1;
private static final int TAG_GEOM_LINE = 2;
private static final int TAG_GEOM_POLYGON = 3;
private short[] mTmpTags = new short[1024];
private Tile mTile;
private final String mLocale;
private ITileDataSink mMapDataCallback;
private static final float REF_TILE_SIZE = 4096.0f;
private float mScale;
public TileDecoder() {
this("");
}
public TileDecoder(String locale) {
mLocale = locale;
}
@Override
public boolean decode(Tile tile, ITileDataSink mapDataCallback, InputStream is)
throws IOException {
if (debug)
log.debug(tile + " decode");
//setInputStream(new InflaterInputStream(is));
setInputStream(is);
mTile = tile;
mMapDataCallback = mapDataCallback;
mScale = REF_TILE_SIZE / Tile.SIZE;
int val;
while (hasData() && (val = decodeVarint32()) > 0) {
// read tag and wire type
int tag = (val >> 3);
switch (tag) {
case TAG_TILE_LAYERS:
decodeLayer();
break;
default:
error(mTile + " invalid type for tile: " + tag);
return false;
}
}
if (hasData()) {
error(tile + " invalid tile");
return false;
}
return true;
}
private boolean decodeLayer() throws IOException {
//int version = 0;
//int extent = 4096;
int bytes = decodeVarint32();
ArrayList keys = new ArrayList<>();
ArrayList values = new ArrayList<>();
String name = null;
int numFeatures = 0;
ArrayList features = new ArrayList<>();
int end = position() + bytes;
while (position() < end) {
// read tag and wire type
int val = decodeVarint32();
if (val == 0)
break;
int tag = (val >> 3);
switch (tag) {
case TAG_LAYER_KEYS:
keys.add(decodeString());
break;
case TAG_LAYER_VALUES:
values.add(decodeValue());
break;
case TAG_LAYER_FEATURES:
numFeatures++;
decodeFeature(features);
break;
case TAG_LAYER_VERSION:
//version =
decodeVarint32();
break;
case TAG_LAYER_NAME:
name = decodeString();
break;
case TAG_LAYER_EXTENT:
//extent =
decodeVarint32();
break;
default:
error(mTile + " invalid type for layer: " + tag);
break;
}
}
Tag layerTag = new Tag("layer", name);
if (debug)
log.debug("add layer " + name);
if (numFeatures == 0)
return true;
//int[] ignoreLocal = new int[1000];
int numIgnore = 0;
int fallBackLocal = -1;
int matchedLocal = -1;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if (!key.startsWith(Tag.KEY_NAME))
continue;
int len = key.length();
if (len == 4) {
fallBackLocal = i;
continue;
}
if (len < 7) {
//ignoreLocal[numIgnore++] = i;
continue;
}
if (mLocale.equals(key.substring(5))) {
//log.debug("found local " + key);
matchedLocal = i;
} //else
//ignoreLocal[numIgnore++] = i;
}
for (Feature f : features) {
if (f.elem.type == GeometryType.NONE)
continue;
f.elem.tags.clear();
f.elem.tags.add(layerTag);
boolean hasName = false;
String fallbackName = null;
for (int j = 0; j < (f.numTags << 1); j += 2) {
int keyIdx = f.tags[j];
//for (int i = 0; i < numIgnore; i++)
// if (keyIdx == ignoreLocal[i])
// continue tagLoop;
if (keyIdx == fallBackLocal) {
fallbackName = values.get(f.tags[j + 1]);
continue;
}
String key;
String val = values.get(f.tags[j + 1]);
if (keyIdx == matchedLocal) {
hasName = true;
f.elem.tags.add(new Tag(Tag.KEY_NAME, val, false));
} else {
key = keys.get(keyIdx);
if (key.startsWith(Tag.KEY_NAME))
continue;
f.elem.tags.add(new Tag(key, val));
}
}
if (!hasName && fallbackName != null)
f.elem.tags.add(new Tag(Tag.KEY_NAME, fallbackName, false));
// Calculate height of building parts
if (!f.elem.tags.containsKey(Tag.KEY_HEIGHT)) {
if (f.elem.tags.containsKey(Tag.KEY_VOLUME)
&& f.elem.tags.containsKey(Tag.KEY_AREA)) {
float volume = Float.parseFloat(f.elem.tags.getValue(Tag.KEY_VOLUME));
float area = Float.parseFloat(f.elem.tags.getValue(Tag.KEY_AREA));
String heightStr = String.valueOf(FastMath.round2(volume / area));
f.elem.tags.add(new Tag(Tag.KEY_HEIGHT, heightStr, false));
}
}
// FIXME extract layer tag here
f.elem.setLayer(5);
mMapDataCallback.process(f.elem);
f = mFeaturePool.release(f);
}
return true;
}
private final Pool mFeaturePool = new Pool() {
int count;
@Override
protected Feature createItem() {
count++;
return new Feature();
}
@Override
protected boolean clearItem(Feature item) {
if (count > 100) {
count--;
return false;
}
item.elem.tags.clear();
item.elem.clear();
item.tags = null;
item.type = 0;
item.numTags = 0;
return true;
}
};
static class Feature extends Inlist {
short[] tags;
int numTags;
int type;
final MapElement elem;
Feature() {
elem = new MapElement();
}
boolean match(short otherTags[], int otherNumTags, int otherType) {
if (numTags != otherNumTags)
return false;
if (type != otherType)
return false;
for (int i = 0; i < numTags << 1; i++) {
if (tags[i] != otherTags[i])
return false;
}
return true;
}
}
private void decodeFeature(ArrayList features) throws IOException {
int bytes = decodeVarint32();
int end = position() + bytes;
int type = 0;
//long id;
lastX = 0;
lastY = 0;
mTmpTags[0] = -1;
Feature curFeature = null;
int numTags = 0;
//log.debug("start feature");
while (position() < end) {
// read tag and wire type
int val = decodeVarint32();
if (val == 0)
break;
int tag = (val >>> 3);
switch (tag) {
case TAG_FEATURE_ID:
//id =
decodeVarint32();
break;
case TAG_FEATURE_TAGS:
mTmpTags = decodeUnsignedVarintArray(mTmpTags);
for (; numTags < mTmpTags.length && mTmpTags[numTags] >= 0; )
numTags += 2;
numTags >>= 1;
break;
case TAG_FEATURE_TYPE:
type = decodeVarint32();
//log.debug("got type " + type);
break;
case TAG_FEATURE_GEOMETRY:
for (Feature f : features) {
if (f.match(mTmpTags, numTags, type)) {
curFeature = f;
break;
}
}
if (curFeature == null) {
curFeature = mFeaturePool.get();
curFeature.tags = new short[numTags << 1];
System.arraycopy(mTmpTags, 0, curFeature.tags, 0, numTags << 1);
curFeature.numTags = numTags;
curFeature.type = type;
features.add(curFeature);
}
decodeCoordinates(type, curFeature);
break;
default:
error(mTile + " invalid type for feature: " + tag);
break;
}
}
}
private static final int CLOSE_PATH = 0x07;
private static final int MOVE_TO = 0x01;
private static final int LINE_TO = 0x02;
private int lastX, lastY;
private int decodeCoordinates(int type, Feature feature) throws IOException {
int bytes = decodeVarint32();
fillBuffer(bytes);
if (feature == null) {
bufferPos += bytes;
return 0;
}
MapElement elem = feature.elem;
boolean isPoint = false;
boolean isPoly = false;
boolean isLine = false;
if (type == TAG_GEOM_LINE) {
elem.startLine();
isLine = true;
} else if (type == TAG_GEOM_POLYGON) {
elem.startPolygon();
isPoly = true;
} else if (type == TAG_GEOM_POINT) {
isPoint = true;
elem.startPoints();
} else if (type == TAG_GEOM_UNKNOWN)
elem.startPoints();
int val;
int curX = 0;
int curY = 0;
int prevX = 0;
int prevY = 0;
int cmd = 0;
int repeat = 0, cnt = 0;
boolean first = true;
boolean lastClip = false;
// test bbox for outer..
boolean isOuter = true;
boolean simplify = false; //mTile.zoomLevel < 14;
int pixel = simplify ? 7 : 3;
//int xmin = Integer.MAX_VALUE, xmax = Integer.MIN_VALUE;
//int ymin = Integer.MAX_VALUE, ymax = Integer.MIN_VALUE;
for (int end = bufferPos + bytes; bufferPos < end; ) {
if (repeat == 0) {
val = decodeVarint32Filled();
// number of points
repeat = val >>> 3;
cnt = 0;
// path command
cmd = val & 0x07;
if (isLine && lastClip) {
elem.addPoint(curX / mScale, curY / mScale);
lastClip = false;
}
if (cmd == CLOSE_PATH) {
repeat = 0;
elem.startHole();
continue;
}
// if (first) {
// first = false;
// continue;
// }
if (cmd == MOVE_TO) {
if (type == TAG_GEOM_LINE) {
elem.startLine();
}
}
//elem.startHole();
// } else if (type == TAG_GEOM_POLYGON) {
// isOuter = false;
// elem.startHole();
// }
// }
// continue;
if (repeat == 0)
continue;
}
repeat--;
val = decodeVarint32Filled();
int s = ((val >>> 1) ^ -(val & 1));
curX = lastX = lastX + s;
val = decodeVarint32Filled();
s = ((val >>> 1) ^ -(val & 1));
curY = lastY = lastY + s;
int dx = (curX - prevX);
int dy = (curY - prevY);
prevX = curX;
prevY = curY;
//if (isPoly && repeat == 0 && cnt > 0) {
// prevX = curX;
// prevY = curY;
// only add last point if it is di
//int ppos = cnt * 2;
//if (elem.points[elem.pointPos - ppos] != curX
// || elem.points[elem.pointPos - ppos + 1] != curY)
// elem.addPoint(curX / mScale, curY / mScale);
//lastClip = false;
//continue;
//}
if ((isPoint || cmd == MOVE_TO || cmd == LINE_TO)) {
elem.addPoint(curX / mScale, curY / mScale);
cnt++;
lastClip = false;
continue;
}
lastClip = true;
}
// if (isPoly && isOuter && simplify && !testBBox(xmax - xmin, ymax - ymin)) {
// //log.debug("skip small poly "+ elem.indexPos + " > "
// // + (xmax - xmin) * (ymax - ymin));
// elem.pointPos -= elem.index[elem.indexPos];
// if (elem.indexPos > 0) {
// elem.indexPos -= 2;
// elem.index[elem.indexPos + 1] = -1;
// } else {
// elem.type = GeometryType.NONE;
// }
// return 0;
// }
if (isLine && lastClip)
elem.addPoint(curX / mScale, curY / mScale);
return 1;
}
private String decodeValue() throws IOException {
int bytes = decodeVarint32();
String value = null;
int end = position() + bytes;
while (position() < end) {
// read tag and wire type
int val = decodeVarint32();
if (val == 0)
break;
int tag = (val >> 3);
switch (tag) {
case TAG_VALUE_STRING:
value = decodeString();
break;
case TAG_VALUE_UINT:
value = String.valueOf(decodeVarint32());
break;
case TAG_VALUE_SINT:
value = String.valueOf(deZigZag(decodeVarint32()));
break;
case TAG_VALUE_LONG:
value = String.valueOf(decodeVarint64());
break;
case TAG_VALUE_FLOAT:
value = String.valueOf(decodeFloat());
break;
case TAG_VALUE_DOUBLE:
value = String.valueOf(decodeDouble());
break;
case TAG_VALUE_BOOL:
value = decodeBool() ? "yes" : "no";
break;
default:
break;
}
}
return value;
}
}