/* * Copyright 2013 Hannes Janetzek * * 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 <http://www.gnu.org/licenses/>. */ package org.oscim.tiling.source.oscimap2; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.MapElement; import org.oscim.core.Tag; import org.oscim.core.TagSet; import org.oscim.core.Tile; import org.oscim.tiling.ITileDataSink; import org.oscim.tiling.ITileDataSource; import org.oscim.tiling.source.PbfDecoder; import org.oscim.tiling.source.UrlTileDataSource; import org.oscim.tiling.source.UrlTileSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class OSciMap2TileSource extends UrlTileSource { public OSciMap2TileSource(String url) { super(url, "/{Z}/{X}/{Y}.osmtile"); } @Override public ITileDataSource getDataSource() { return new UrlTileDataSource(this, new TileDecoder(), getHttpEngine()); } static class TileDecoder extends PbfDecoder { static final Logger log = LoggerFactory.getLogger(TileDecoder.class); private static final int TAG_TILE_NUM_TAGS = 1; private static final int TAG_TILE_TAG_KEYS = 2; private static final int TAG_TILE_TAG_VALUES = 3; private static final int TAG_TILE_LINE = 11; private static final int TAG_TILE_POLY = 12; private static final int TAG_TILE_POINT = 13; // private static final int TAG_TILE_LABEL = 21; // private static final int TAG_TILE_WATER = 31; private static final int TAG_ELEM_NUM_INDICES = 1; private static final int TAG_ELEM_TAGS = 11; private static final int TAG_ELEM_INDEX = 12; private static final int TAG_ELEM_COORDS = 13; private static final int TAG_ELEM_LAYER = 21; private static final int TAG_ELEM_HEIGHT = 31; private static final int TAG_ELEM_MIN_HEIGHT = 32; private static final int TAG_ELEM_PRIORITY = 41; private int[] mSArray; private final TagSet mTileTags; private final MapElement mElem; private Tile mTile; private ITileDataSink mMapDataSink; // scale coordinates to tile size private final static float REF_TILE_SIZE = 4096.0f; private float mScale; TileDecoder() { mElem = new MapElement(); mTileTags = new TagSet(20); // temp array for decoding shorts mSArray = new int[100]; } @Override public boolean decode(Tile tile, ITileDataSink sink, InputStream is) throws IOException { readUnsignedInt(is, buffer); setInputStream(is); mTile = tile; mMapDataSink = sink; mScale = REF_TILE_SIZE / Tile.SIZE; mTileTags.clear(); int val; int numTags = 0; while (hasData() && (val = decodeVarint32()) > 0) { // read tag and wire type int tag = (val >> 3); switch (tag) { case TAG_TILE_NUM_TAGS: numTags = decodeVarint32(); break; case TAG_TILE_TAG_KEYS: int len = numTags; if (mSArray.length < len) mSArray = new int[len]; decodeVarintArray(numTags, mSArray); break; case TAG_TILE_TAG_VALUES: // this wastes one byte, as there is no packed string... decodeTileTags(); break; case TAG_TILE_LINE: case TAG_TILE_POLY: case TAG_TILE_POINT: decodeTileElement(tag); break; default: log.debug(mTile + " invalid type for tile: " + tag); return false; } } return true; } private boolean decodeTileTags() throws IOException { String tagString = decodeString(); int curTag = mTileTags.numTags; String key = Tags.keys[mSArray[curTag]]; Tag tag; if (key == Tag.KEY_NAME) tag = new Tag(key, tagString, false); else tag = new Tag(key, tagString, true); if (debug) log.debug(mTile + " add tag: " + curTag + " " + tag); mTileTags.add(tag); return true; } private int decodeWayIndices(int indexCnt) throws IOException { mElem.ensureIndexSize(indexCnt, false); decodeVarintArray(indexCnt, mElem.index); int[] index = mElem.index; int coordCnt = 0; for (int i = 0; i < indexCnt; i++) { coordCnt += index[i]; index[i] *= 2; } // set end marker if (indexCnt < index.length) index[indexCnt] = -1; return coordCnt; } private boolean decodeTileElement(int type) throws IOException { int bytes = decodeVarint32(); short[] index = null; int end = position() + bytes; int indexCnt = 1; boolean fail = false; int coordCnt = 0; if (type == TAG_TILE_POINT) { coordCnt = 1; mElem.index[0] = 2; } mElem.layer = 5; //mElem.height = 0; //mElem.minHeight = 0; while (position() < end) { // read tag and wire type int val = decodeVarint32(); if (val == 0) break; int tag = (val >> 3); switch (tag) { case TAG_ELEM_TAGS: if (!decodeElementTags()) return false; break; case TAG_ELEM_NUM_INDICES: indexCnt = decodeVarint32(); break; case TAG_ELEM_INDEX: coordCnt = decodeWayIndices(indexCnt); break; case TAG_ELEM_COORDS: if (coordCnt == 0) { log.debug(mTile + " no coordinates"); } mElem.ensurePointSize(coordCnt, false); int cnt = decodeInterleavedPoints(mElem, mScale); if (cnt != coordCnt) { log.debug(mTile + " wrong number of coordintes"); fail = true; } break; case TAG_ELEM_LAYER: mElem.layer = decodeVarint32(); break; case TAG_ELEM_HEIGHT: //mElem.height = decodeVarint32(); break; case TAG_ELEM_MIN_HEIGHT: //mElem.minHeight = decodeVarint32(); break; case TAG_ELEM_PRIORITY: //mElem.priority = decodeVarint32(); break; default: log.debug(mTile + " invalid type for way: " + tag); } } if (fail || indexCnt == 0) { log.debug(mTile + " failed reading way: bytes:" + bytes + " index:" + (Arrays.toString(index)) + " tag:" + (mElem.tags.numTags > 0 ? Arrays.deepToString(mElem.tags.tags) : "null") + " " + indexCnt + " " + coordCnt); return false; } switch (type) { case TAG_TILE_LINE: mElem.type = GeometryType.LINE; break; case TAG_TILE_POLY: mElem.type = GeometryType.POLY; break; case TAG_TILE_POINT: mElem.type = GeometryType.POINT; break; } mMapDataSink.process(mElem); return true; } private boolean decodeElementTags() throws IOException { int bytes = decodeVarint32(); mElem.tags.clear(); int cnt = 0; int end = position() + bytes; int max = mTileTags.numTags - 1; for (; position() < end; cnt++) { int tagNum = decodeVarint32(); if (tagNum < 0) { log.debug("NULL TAG: " + mTile + " invalid tag:" + tagNum + " " + cnt); return false; } if (tagNum < Tags.MAX) { mElem.tags.add(Tags.tags[tagNum]); continue; } tagNum -= Tags.LIMIT; if (tagNum < 0 || tagNum > max) { log.debug("NULL TAG: " + mTile + " could not find tag:" + tagNum + " " + cnt); return false; } mElem.tags.add(mTileTags.tags[tagNum]); } if (cnt == 0) { log.debug("got no TAG!"); return false; } return true; } } }