no.ecc.vectortile.VectorTileDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of graphhopper-web-bundle Show documentation
Show all versions of graphhopper-web-bundle Show documentation
Use the GraphHopper routing engine as a web-service
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package no.ecc.vectortile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
import vector_tile.VectorTile;
import vector_tile.VectorTile.Tile.GeomType;
import vector_tile.VectorTile.Tile.Layer;
public class VectorTileDecoder {
private boolean autoScale = true;
/**
* Get the autoScale setting.
*
* @return autoScale
*/
public boolean isAutoScale() {
return autoScale;
}
/**
* Set the autoScale setting.
*
* @param autoScale
* when true, the encoder automatically scale and return all coordinates in the 0..255 range.
* when false, the encoder returns all coordinates in the 0..extent-1 range as they are encoded.
*
*/
public void setAutoScale(boolean autoScale) {
this.autoScale = autoScale;
}
public FeatureIterable decode(byte[] data) throws IOException {
return decode(data, Filter.ALL);
}
public FeatureIterable decode(byte[] data, String layerName) throws IOException {
return decode(data, new Filter.Single(layerName));
}
public FeatureIterable decode(byte[] data, Set layerNames) throws IOException {
return decode(data, new Filter.Any(layerNames));
}
public FeatureIterable decode(byte[] data, Filter filter) throws IOException {
VectorTile.Tile tile = VectorTile.Tile.parseFrom(data);
return new FeatureIterable(tile, filter, autoScale);
}
static int zigZagDecode(int n) {
return ((n >> 1) ^ (-(n & 1)));
}
static Geometry decodeGeometry(GeometryFactory gf, GeomType geomType, List commands, double scale) {
int x = 0;
int y = 0;
List> coordsList = new ArrayList>();
List coords = null;
int geometryCount = commands.size();
int length = 0;
int command = 0;
int i = 0;
while (i < geometryCount) {
if (length <= 0) {
length = commands.get(i++).intValue();
command = length & ((1 << 3) - 1);
length = length >> 3;
}
if (length > 0) {
if (command == Command.MoveTo) {
coords = new ArrayList();
coordsList.add(coords);
}
if (command == Command.ClosePath) {
if (geomType != VectorTile.Tile.GeomType.POINT && !coords.isEmpty()) {
coords.add(new Coordinate(coords.get(0)));
}
length--;
continue;
}
int dx = commands.get(i++).intValue();
int dy = commands.get(i++).intValue();
length--;
dx = zigZagDecode(dx);
dy = zigZagDecode(dy);
x = x + dx;
y = y + dy;
Coordinate coord = new Coordinate(x / scale, y / scale);
coords.add(coord);
}
}
Geometry geometry = null;
switch (geomType) {
case LINESTRING:
List lineStrings = new ArrayList();
for (List cs : coordsList) {
if (cs.size() <= 1) {
continue;
}
lineStrings.add(gf.createLineString(cs.toArray(new Coordinate[cs.size()])));
}
if (lineStrings.size() == 1) {
geometry = lineStrings.get(0);
} else if (lineStrings.size() > 1) {
geometry = gf.createMultiLineString(lineStrings.toArray(new LineString[lineStrings.size()]));
}
break;
case POINT:
List allCoords = new ArrayList();
for (List cs : coordsList) {
allCoords.addAll(cs);
}
if (allCoords.size() == 1) {
geometry = gf.createPoint(allCoords.get(0));
} else if (allCoords.size() > 1) {
geometry = gf.createMultiPointFromCoords(allCoords.toArray(new Coordinate[allCoords.size()]));
}
break;
case POLYGON:
List> polygonRings = new ArrayList>();
List ringsForCurrentPolygon = null;
Boolean ccw = null;
for (List cs : coordsList) {
Coordinate[] ringCoords = cs.toArray(new Coordinate[cs.size()]);
double area = Area.ofRingSigned(ringCoords);
if (area == 0) {
continue;
}
boolean thisCcw = area < 0;
if (ccw == null) {
ccw = thisCcw;
}
LinearRing ring = gf.createLinearRing(ringCoords);
if (ccw == thisCcw) {
if (ringsForCurrentPolygon != null) {
polygonRings.add(ringsForCurrentPolygon);
}
ringsForCurrentPolygon = new ArrayList<>();
}
ringsForCurrentPolygon.add(ring);
}
if (ringsForCurrentPolygon != null) {
polygonRings.add(ringsForCurrentPolygon);
}
List polygons = new ArrayList();
for (List rings : polygonRings) {
LinearRing shell = rings.get(0);
LinearRing[] holes = rings.subList(1, rings.size()).toArray(new LinearRing[rings.size() - 1]);
polygons.add(gf.createPolygon(shell, holes));
}
if (polygons.size() == 1) {
geometry = polygons.get(0);
}
if (polygons.size() > 1) {
geometry = gf.createMultiPolygon(GeometryFactory.toPolygonArray(polygons));
}
break;
case UNKNOWN:
break;
default:
break;
}
if (geometry == null) {
geometry = gf.createGeometryCollection(new Geometry[0]);
}
return geometry;
}
public static final class FeatureIterable implements Iterable {
private final VectorTile.Tile tile;
private final Filter filter;
private boolean autoScale;
public FeatureIterable(VectorTile.Tile tile, Filter filter, boolean autoScale) {
this.tile = tile;
this.filter = filter;
this.autoScale = autoScale;
}
public Iterator iterator() {
return new FeatureIterator(tile, filter, autoScale);
}
public List asList() {
List features = new ArrayList();
for (Feature feature : this) {
features.add(feature);
}
return features;
}
public Collection getLayerNames() {
Set layerNames = new HashSet();
for (VectorTile.Tile.Layer layer : tile.getLayersList()) {
layerNames.add(layer.getName());
}
return Collections.unmodifiableSet(layerNames);
}
}
private static final class FeatureIterator implements Iterator {
private final GeometryFactory gf = new GeometryFactory();
private final Filter filter;
private final Iterator layerIterator;
private Iterator featureIterator;
private int extent;
private String layerName;
private double scale;
private boolean autoScale;
private final List keys = new ArrayList();
private final List