Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e5cff90
use 4 or 16 cells only for LocationIndexTree
karussell Oct 15, 2018
f0e40ef
make it possible to visualize boundaries of Quadtree and add few note…
karussell Oct 15, 2018
d2458ef
implement query(BBox), #1324
karussell Oct 15, 2018
2c031d2
test and show how to properly user query(BBox)
karussell Oct 15, 2018
d8d6b9f
make it clear what we can expect from the Visitor functionality; adde…
karussell Oct 16, 2018
f68cd97
Merge branch 'master' into loc_index
karussell Oct 16, 2018
4ccc7d5
use query(Shape) instead of query(BBox)
karussell Nov 14, 2018
81de4f2
example log: avoid immediateFlush config as it is confusing
karussell Mar 16, 2019
de24f4a
make current maximum precision clear in javadoc
karussell Mar 18, 2019
1621c48
show tiles via graph exploration
karussell Mar 18, 2019
95e6555
merged branch loc_index
karussell Mar 18, 2019
eb48989
made vector tiles much faster via loc_index branch
karussell Mar 18, 2019
1025393
show less details for small zoom numbers and full geometry details fo…
karussell Mar 19, 2019
924b0bf
make it possible to use speed in JavaScript and use Leaflet > 1.0; se…
karussell Mar 19, 2019
6cdd50d
store name and filter based on speed not edge length
karussell Mar 19, 2019
ccbcd99
use separate MVTResource
karussell Mar 19, 2019
a9db7d3
try to fix openjdk12 download URL
karussell Mar 19, 2019
deb8fe8
merged master
karussell Mar 19, 2019
e23fd66
merged master
karussell Mar 19, 2019
62dba6f
Adds alternative z/x/y endpoint.
easbar Mar 23, 2019
c4df0e2
fix response application type
karussell Mar 23, 2019
39d1393
remove unnecessary isochrone stuff
karussell Mar 23, 2019
ba9a703
integrate local mvt into GH maps demo
karussell Mar 24, 2019
19d07da
switch localhost to 127.0.0.1
karussell Mar 24, 2019
2b638b5
do remove roads from omniscale layer
karussell Mar 25, 2019
4e800d7
Merge branch 'master' into experimental-mvt
karussell May 31, 2019
eedda41
updated main.js
karussell May 31, 2019
829a4dc
fixed merge conflict for LocationIndexTree
karussell May 31, 2019
76964db
include mvt test that reads tile from /mvt endpoint
karussell May 31, 2019
4086e0b
revert changes in travis config
karussell May 31, 2019
82fd4c2
Merge branch 'master' into experimental-mvt
karussell Jun 15, 2019
bf3ece8
include various EncodedValues in response
karussell Jun 15, 2019
ce9bcd9
advertise /mvt endpoint
karussell Jun 16, 2019
2ec2309
Merge branch 'master' into experimental-mvt
karussell Jun 16, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,4 @@ Here is a list of the more detailed features including a link to the documentati
* Do [map matching](https://github.com/graphhopper/map-matching) with GraphHopper
* Calculate [isochrones](./docs/web/api-doc.md#isochrone) with GraphHopper
* Show path details [#1142](https://github.com/graphhopper/graphhopper/pull/1142)
* GraphHopper can produce vector tiles for debugging purposes [#1572](https://github.com/graphhopper/graphhopper/pull/1572)
Original file line number Diff line number Diff line change
Expand Up @@ -666,9 +666,6 @@ public void applyWayTags(ReaderWay way, EdgeIteratorState edge) {
}
}

/**
* The returned list is never empty.
*/
public List<FlagEncoder> fetchEdgeEncoders() {
return new ArrayList<FlagEncoder>(edgeEncoders);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ public class LocationIndexTree implements LocationIndex {
private final int MAGIC_INT;
private final NodeAccess nodeAccess;
protected DistanceCalc distCalc = Helper.DIST_PLANE;
protected SpatialKeyAlgo keyAlgo;
int maxRegionSearch = 4;
SpatialKeyAlgo keyAlgo;
private int maxRegionSearch = 4;
private DistanceCalc preciseDistCalc = Helper.DIST_EARTH;
private int[] entries;
private byte[] shifts;
Expand Down Expand Up @@ -138,7 +138,7 @@ void prepareAlgo() {
equalNormedDelta = distCalc.calcNormalizedDist(0.1);

// now calculate the necessary maxDepth d for our current bounds
// if we assume a minimum resolution like 0.5km for a leaf-tile
// if we assume a minimum resolution like 0.5km for a leaf-tile
// n^(depth/2) = toMeter(dLon) / minResolution
BBox bounds = graph.getBounds();
if (graph.getNodes() == 0)
Expand Down Expand Up @@ -349,8 +349,8 @@ IntArrayList getEntries() {
/**
* This method fills the set with stored node IDs from the given spatial key part (a latitude-longitude prefix).
*/
final void fillIDs(long keyPart, int intIndex, GHIntHashSet set, int depth) {
long pointer = (long) intIndex << 2;
final void fillIDs(long keyPart, int intPointer, GHIntHashSet set, int depth) {
long pointer = (long) intPointer << 2;
if (depth == entries.length) {
int nextIntPointer = dataAccess.getInt(pointer);
if (nextIntPointer < 0) {
Expand Down Expand Up @@ -385,7 +385,6 @@ final long createReverseKey(long key) {
/**
* calculate the distance to the nearest tile border for a given lat/lon coordinate in the
* context of a spatial key tile.
* <p>
*/
final double calculateRMin(double lat, double lon) {
return calculateRMin(lat, lon, 0);
Expand Down Expand Up @@ -788,7 +787,7 @@ IntArrayList getResults() {

// Space efficient sorted integer set. Suited for only a few entries.
static class SortedIntSet extends IntArrayList {
public SortedIntSet(int capacity) {
SortedIntSet(int capacity) {
super(capacity);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@
import com.graphhopper.util.shapes.GHPoint;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;

import static org.junit.Assert.*;

Expand Down
8 changes: 6 additions & 2 deletions 8 isochrone/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@
<version>0.13-SNAPSHOT</version>
</parent>


<dependencies>
<dependency>
<groupId>com.graphhopper</groupId>
<artifactId>graphhopper-reader-osm</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>com.wdtinc</groupId>
<artifactId>mapbox-vector-tile</artifactId>
<version>3.1.0</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
Expand All @@ -44,7 +49,6 @@
<scope>test</scope>
</dependency>
</dependencies>

</project>


Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ protected void configure() {
if (configuration.getBool("web.change_graph.enabled", false)) {
environment.jersey().register(ChangeGraphResource.class);
}

environment.jersey().register(MVTResource.class);
environment.jersey().register(NearestResource.class);
environment.jersey().register(RouteResource.class);
environment.jersey().register(IsochroneResource.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void start() {
graphHopper.importOrLoad();
logger.info("loaded graph at:" + graphHopper.getGraphHopperLocation()
+ ", data_reader_file:" + graphHopper.getDataReaderFile()
+ ", flag_encoders:" + graphHopper.getEncodingManager()
+ ", encoded values:" + graphHopper.getEncodingManager().toEncodedValuesAsString()
+ ", " + graphHopper.getGraphHopperStorage().toDetailsString());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@ private Response jsonSuccessResponse(Object result, float took) {
info.put("took", Math.round(took * 1000));
return Response.ok(json).build();
}
}
}
167 changes: 167 additions & 0 deletions 167 web-bundle/src/main/java/com/graphhopper/resources/MVTResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package com.graphhopper.resources;

import com.graphhopper.GraphHopper;
import com.graphhopper.routing.profiles.*;
import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.storage.index.LocationIndexTree;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.BBox;
import com.wdtinc.mapbox_vector_tile.VectorTile;
import com.wdtinc.mapbox_vector_tile.adapt.jts.IGeometryFilter;
import com.wdtinc.mapbox_vector_tile.adapt.jts.JtsAdapter;
import com.wdtinc.mapbox_vector_tile.adapt.jts.TileGeomResult;
import com.wdtinc.mapbox_vector_tile.adapt.jts.UserDataKeyValueMapConverter;
import com.wdtinc.mapbox_vector_tile.build.MvtLayerBuild;
import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams;
import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Path("mvt")
public class MVTResource {

private static final Logger logger = LoggerFactory.getLogger(MVTResource.class);
private static final MediaType PBF = new MediaType("application", "x-protobuf");
private final GraphHopper graphHopper;
private final EncodingManager encodingManager;

@Inject
public MVTResource(GraphHopper graphHopper, EncodingManager encodingManager) {
this.graphHopper = graphHopper;
this.encodingManager = encodingManager;
}

@GET
@Path("{z}/{x}/{y}.mvt")
@Produces("application/x-protobuf")
public Response doGetXyz(
@Context HttpServletRequest httpReq,
@Context UriInfo uriInfo,
@PathParam("z") int zInfo,
@PathParam("x") int xInfo,
@PathParam("y") int yInfo,
@QueryParam(Parameters.DETAILS.PATH_DETAILS) List<String> pathDetails) {

if (zInfo <= 9) {
VectorTile.Tile.Builder mvtBuilder = VectorTile.Tile.newBuilder();
return Response.fromResponse(Response.ok(mvtBuilder.build().toByteArray(), PBF).build())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With maputnik this gives me Error: http status 200 returned without content. Can we do better at returning 'an empty tile' ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be possible to convert an empty geometry to an MVT tile.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I cannot reproduce this problem (and also do not know how to create an empty geometry), can we delay and fix this in a later issue?

.header("X-GH-Took", "0")
.build();
}

StopWatch totalSW = new StopWatch().start();
Coordinate nw = num2deg(xInfo, yInfo, zInfo);
Coordinate se = num2deg(xInfo + 1, yInfo + 1, zInfo);
LocationIndexTree locationIndex = (LocationIndexTree) graphHopper.getLocationIndex();
final NodeAccess na = graphHopper.getGraphHopperStorage().getNodeAccess();
EdgeExplorer edgeExplorer = graphHopper.getGraphHopperStorage().createEdgeExplorer(DefaultEdgeFilter.ALL_EDGES);
BBox bbox = new BBox(nw.x, se.x, se.y, nw.y);
if (!bbox.isValid())
throw new IllegalStateException("Invalid bbox " + bbox);

final GeometryFactory geometryFactory = new GeometryFactory();
VectorTile.Tile.Builder mvtBuilder = VectorTile.Tile.newBuilder();
final IGeometryFilter acceptAllGeomFilter = geometry -> true;
final Envelope tileEnvelope = new Envelope(se, nw);
final MvtLayerParams layerParams = new MvtLayerParams(256, 4096);
final UserDataKeyValueMapConverter converter = new UserDataKeyValueMapConverter();
if (!encodingManager.hasEncodedValue(RoadClass.KEY))
throw new IllegalStateException("You need to configure GraphHopper to store road_class, e.g. graph.encoded_values: road_class,max_speed,... ");

final EnumEncodedValue<RoadClass> roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
final AtomicInteger edgeCounter = new AtomicInteger(0);
// in toFeatures addTags of the converter is called and layerProps is filled with keys&values => those need to be stored in the layerBuilder
// otherwise the decoding won't be successful and "undefined":"undefined" instead of "speed": 30 is the result
final MvtLayerProps layerProps = new MvtLayerProps();
final VectorTile.Tile.Layer.Builder layerBuilder = MvtLayerBuild.newLayerBuilder("roads", layerParams);

locationIndex.query(bbox, new LocationIndexTree.EdgeVisitor(edgeExplorer) {
@Override
public void onEdge(EdgeIteratorState edge, int nodeA, int nodeB) {
LineString lineString;
RoadClass rc = edge.get(roadClassEnc);
if (zInfo >= 14) {
PointList pl = edge.fetchWayGeometry(3);
lineString = pl.toLineString(false);
} else if (rc == RoadClass.MOTORWAY
|| zInfo > 10 && (rc == RoadClass.PRIMARY || rc == RoadClass.TRUNK)
|| zInfo > 11 && (rc == RoadClass.SECONDARY)
|| zInfo > 12) {
double lat = na.getLatitude(nodeA);
double lon = na.getLongitude(nodeA);
double toLat = na.getLatitude(nodeB);
double toLon = na.getLongitude(nodeB);
lineString = geometryFactory.createLineString(new Coordinate[]{new Coordinate(lon, lat), new Coordinate(toLon, toLat)});
} else {
// skip edge for certain zoom
return;
}

edgeCounter.incrementAndGet();
Map<String, Object> map = new HashMap<>(2);
map.put("name", edge.getName());
for (String str : pathDetails) {
// how to indicate an erroneous parameter?
if (str.contains(",") || !encodingManager.hasEncodedValue(str))
continue;

EncodedValue ev = encodingManager.getEncodedValue(str, EncodedValue.class);
if (ev instanceof EnumEncodedValue)
map.put(ev.getName(), edge.get((EnumEncodedValue) ev).toString());
else if (ev instanceof DecimalEncodedValue)
map.put(ev.getName(), edge.get((DecimalEncodedValue) ev));
else if (ev instanceof BooleanEncodedValue)
map.put(ev.getName(), edge.get((BooleanEncodedValue) ev));
else if (ev instanceof IntEncodedValue)
map.put(ev.getName(), edge.get((IntEncodedValue) ev));
}

lineString.setUserData(map);

// doing some AffineTransformation
TileGeomResult tileGeom = JtsAdapter.createTileGeom(lineString, tileEnvelope, geometryFactory, layerParams, acceptAllGeomFilter);
List<VectorTile.Tile.Feature> features = JtsAdapter.toFeatures(tileGeom.mvtGeoms, layerProps, converter);
layerBuilder.addAllFeatures(features);
}

@Override
public void onTile(BBox bbox, int depth) {
}
});

MvtLayerBuild.writeProps(layerBuilder, layerProps);
mvtBuilder.addLayers(layerBuilder.build());
byte[] bytes = mvtBuilder.build().toByteArray();
totalSW.stop();
logger.debug("took: " + totalSW.getSeconds() + ", edges:" + edgeCounter.get());
return Response.ok(bytes, PBF).header("X-GH-Took", "" + totalSW.getSeconds() * 1000)
.build();
}

Coordinate num2deg(int xInfo, int yInfo, int zoom) {
double n = Math.pow(2, zoom);
double lonDeg = xInfo / n * 360.0 - 180.0;
// unfortunately latitude numbers goes from north to south
double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * yInfo / n)));
double latDeg = Math.toDegrees(latRad);
return new Coordinate(lonDeg, latDeg);
}
}
1 change: 1 addition & 0 deletions 1 web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
}
},
"dependencies": {
"leaflet.vectorgrid": "1.3.0",
"browserify": "16.2.0",
"browserify-swap": "0.2.2",
"d3": "5.9.1",
Expand Down
56 changes: 55 additions & 1 deletion 56 web/src/main/resources/assets/js/config/tileLayers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var lyrk = L.tileLayer('https://tiles.lyrk.org/' + (retinaTiles ? 'lr' : 'ls') +
attribution: osmAttr + ', <a href="https://geodienste.lyrk.de/">Lyrk</a>'
});

var omniscale = L.tileLayer('https://maps.omniscale.net/v2/' +osAPIKey + '/style.default' + (retinaTiles ? '/hq.true' : '') + '/{z}/{x}/{y}.png', {
var omniscale = L.tileLayer('https://maps.omniscale.net/v2/' +osAPIKey + '/style.default/{z}/{x}/{y}.png' + (retinaTiles ? '?hq=true' : ''), {
layers: 'osm',
attribution: osmAttr + ', &copy; <a href="https://maps.omniscale.com/">Omniscale</a>'
});
Expand Down Expand Up @@ -85,13 +85,67 @@ var availableTileLayers = {
"OpenStreetMap.de": osmde
};

var overlays;
if(ghenv.environment === 'development') {
var omniscaleGray = L.tileLayer('https://maps.omniscale.net/v2/' +osAPIKey + '/style.grayscale/layers.world,buildings,landusages,labels/{z}/{x}/{y}.png?' + (retinaTiles ? '&hq=true' : ''), {
layers: 'osm',
attribution: osmAttr + ', &copy; <a href="https://maps.omniscale.com/">Omniscale</a>'
});
availableTileLayers["Omniscale Dev"] = omniscaleGray;

require('leaflet.vectorgrid');
overlays = {};
overlays["Local MVT"] = L.vectorGrid.protobuf("http://127.0.0.1:8989/mvt/{z}/{x}/{y}.mvt?details=max_speed&details=road_class&details=road_environment", {
rendererFactory: L.canvas.tile,
maxZoom: 20,
minZoom: 10,
vectorTileLayerStyles: {
'roads': function(properties, zoom) {
var color, opacity = 1, weight = 1, radius = 2, rc = properties.road_class;
// if(properties.speed < 30) console.log(properties)
if(rc == "motorway") {
color = '#dd504b'; // red
weight = 3;
radius = 6;
} else if(rc == "primary" || rc == "trunk") {
color = '#e2a012'; // orange
weight = 2;
radius = 6;
} else if(rc == "secondary") {
weight = 2;
color = '#f7c913'; // yellow
} else {
if(zoom <= 11) {
// color = "white";
color = "#aaa5a7"
opacity = 0.2;
} else {
color = "#aaa5a7"; /* gray */
}
}
return {
weight: weight,
color: color,
opacity: opacity,
radius: radius
}
},
},
interactive: true // use true to make sure that this VectorGrid fires mouse/pointer events
});
}

module.exports.activeLayerName = "Omniscale";
module.exports.defaultLayer = omniscale;

module.exports.getAvailableTileLayers = function () {
return availableTileLayers;
};

module.exports.getOverlays = function () {
return overlays;
};

module.exports.selectLayer = function (layerName) {
var defaultLayer = availableTileLayers[layerName];
if (!defaultLayer)
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.