Skip to content

Commit

Permalink
perf(fastisochrones): apply modifications from regular isochrones (#1658
Browse files Browse the repository at this point in the history
)
  • Loading branch information
sfendrich authored Feb 27, 2024
2 parents 874a01d + ac131b6 commit 9abd435
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 1,442 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ RELEASING:
- fix IN1-JAVA-ORGMOZILLA-1314295 ([#1627](https://github.com/GIScience/openrouteservice/issues/1627))
- log summary stats on traffic mapmatching and use progress bar only in debug mode ([#1647](https://github.com/GIScience/openrouteservice/pull/1647))
- move APIEnums into API module ([#1634](https://github.com/GIScience/openrouteservice/issues/1634))
- performance improvements to isochrone calculations ([#1607](https://github.com/GIScience/openrouteservice/pull/1607))
- performance improvements to isochrone calculations ([#1607](https://github.com/GIScience/openrouteservice/pull/1607)) and ([#1658](https://github.com/GIScience/openrouteservice/pull/1658))
- do not apply speed penalty to roads tagged with "access=destination" when computing isochrones ([#1682](https://github.com/GIScience/openrouteservice/pull/1682))
- backend documentation overhaul ([#1651](https://github.com/GIScience/openrouteservice/pull/1651))
- separate docker image build from test workflow. Build nightly on schedule if there are changes to main ([#1689](https://github.com/GIScience/openrouteservice/pull/1689))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void testPolygon() {
.then()
.body("any { it.key == 'type' }", is(true))
.body("any { it.key == 'features' }", is(true))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(70)).and(lessThan(80))))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(40)).and(lessThan(50))))
.body("features[0].properties.center.size()", is(2))
.body("bbox", hasItems(closeTo(8.652489f, 0.02f), closeTo(49.40263f, 0.02f), closeTo(8.708881f, 0.02f), closeTo(49.447865f, 0.02f)))
.body("features[0].type", is("Feature"))
Expand Down Expand Up @@ -346,7 +346,7 @@ void testIntersections() {
.body("features[1].geometry.type", is("Polygon"))
.body("features[2].type", is("Feature"))
.body("features[2].geometry.type", is("Polygon"))
.body("features[2].geometry.coordinates[0].size()", is(both(greaterThan(65)).and(lessThan(85))))
.body("features[2].geometry.coordinates[0].size()", is(both(greaterThan(35)).and(lessThan(45))))
.body("features[2].properties.contours.size()", is(2))
.body("features[2].properties.containsKey('area')", is(true))
.body("features[0].properties.area", is(both(greaterThan(8000000f)).and(lessThan(15000000f))))
Expand Down Expand Up @@ -379,7 +379,7 @@ void testSmoothingFactor() {
.then()
.body("any { it.key == 'type' }", is(true))
.body("any { it.key == 'features' }", is(true))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(60)).and(lessThan(80))))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(40)).and(lessThan(60))))
.statusCode(200);

body.put("smoothing", "100");
Expand All @@ -392,7 +392,7 @@ void testSmoothingFactor() {
.then()
.body("any { it.key == 'type' }", is(true))
.body("any { it.key == 'features' }", is(true))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(55)).and(lessThan(85))))
.body("features[0].geometry.coordinates[0].size()", is(both(greaterThan(15)).and(lessThan(25))))
.statusCode(200);
}

Expand Down
116 changes: 10 additions & 106 deletions ors-engine/src/main/java/org/heigit/ors/fastisochrones/Contour.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@
import com.graphhopper.util.PointList;
import org.heigit.ors.fastisochrones.partitioning.storage.CellStorage;
import org.heigit.ors.fastisochrones.partitioning.storage.IsochroneNodeStorage;
import org.heigit.ors.isochrones.builders.concaveballs.PointItemVisitor;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.index.quadtree.Quadtree;
import org.opensphere.geometry.algorithm.ConcaveHullOpenSphere;

import java.util.*;
import java.util.stream.Collectors;

import static org.heigit.ors.fastisochrones.partitioning.FastIsochroneParameters.getMaxCellNodesNumber;
import static org.heigit.ors.fastisochrones.partitioning.FastIsochroneParameters.isSupercellsEnabled;
import static org.locationtech.jts.algorithm.hull.ConcaveHull.concaveHullByLength;

/**
* Calculates Outlines (Contour) of cells.
Expand Down Expand Up @@ -101,7 +99,7 @@ public void calculateContour() {
private void handleBaseCells() {
for (IntCursor cellId : isochroneNodeStorage.getCellIds()) {
List<Coordinate> coordinates = createCoordinates(cellId.value);
LineString ring = createContour(coordinates, cellStorage.getNodesOfCell(cellId.value).size() < 1000);
LineString ring = createContour(coordinates);
if (ring == null || ring.getNumPoints() < 2) {
cellStorage.setCellContourOrder(cellId.value, new ArrayList<>(), new ArrayList<>());
continue;
Expand Down Expand Up @@ -132,7 +130,7 @@ private IntObjectMap<IntHashSet> handleSuperCells() {
//Calculate the concave hull for all super cells and super super cells
for (IntObjectCursor<IntHashSet> superCell : superCellsToBaseCells) {
List<Coordinate> superCellCoordinates = createSuperCellCoordinates(superCell.value);
LineString ring = createContour(superCellCoordinates, false);
LineString ring = createContour(superCellCoordinates);
if (ring == null || ring.getNumPoints() < 2) {
cellStorage.setCellContourOrder(superCell.key, new ArrayList<>(), new ArrayList<>());
continue;
Expand Down Expand Up @@ -187,71 +185,12 @@ private List<Coordinate> createSuperCellCoordinates(IntHashSet superCell) {
return superCellCoordinates;
}

private Geometry concHullOfNodes(List<Coordinate> coordinates, boolean useHighDetail) {
double defaultVisitorThreshold = useHighDetail ? 0.00005 : 0.0025;
double defaultSearchWidth = 0.0008;
double defaulPointWidth = 0.005;

List<Coordinate> points = new ArrayList<>((int) (1 / 20.0 * coordinates.size()));
PointItemVisitor visitor = new PointItemVisitor(0, 0, defaultVisitorThreshold);
Quadtree qtree = new Quadtree();
Envelope searchEnv = new Envelope();
TreeSet<Coordinate> treeSet = new TreeSet<>();

for (Coordinate coordinate : coordinates)
addPoint(visitor, points, qtree, searchEnv, treeSet, coordinate.x, coordinate.y, defaultSearchWidth, defaulPointWidth, true);

GeometryFactory geomFactory = new GeometryFactory();
int size = points.size();
Geometry[] geometries = new Geometry[size];
int g = 0;
for (Coordinate point : points)
geometries[g++] = geomFactory.createPoint(point);
GeometryCollection treePoints = new GeometryCollection(geometries, geomFactory);

ConcaveHullOpenSphere ch = new ConcaveHullOpenSphere(treePoints, CONCAVE_HULL_THRESHOLD, false);
return ch.getConcaveHull();
}
private Geometry concHullOfNodes(List<Coordinate> points) {
var geomFactory = new GeometryFactory();
var geometries = points.stream().map(geomFactory::createPoint).toArray(Geometry[]::new);
var treePoints = geomFactory.createGeometryCollection(geometries);

private Boolean addPoint(PointItemVisitor visitor,
List<Coordinate> points,
Quadtree tree,
Envelope searchEnv,
Set<Coordinate> treeSet,
double lon,
double lat,
double searchWidth,
double pointWidth,
boolean checkNeighbours) {
if (checkNeighbours) {
visitor.setPoint(lon, lat);
searchEnv.init(lon - searchWidth, lon + searchWidth, lat - searchWidth, lat + searchWidth);
tree.query(searchEnv, visitor);
if (!visitor.isNeighbourFound()) {
Coordinate p = new Coordinate(lon, lat);

if (!treeSet.contains(p)) {
Envelope env = new Envelope(lon - pointWidth, lon + pointWidth, lat - pointWidth, lat + pointWidth);
tree.insert(env, p);
points.add(p);
treeSet.add(p);

return true;
}
}
} else {
Coordinate p = new Coordinate(lon, lat);
if (!treeSet.contains(p)) {
Envelope env = new Envelope(lon - pointWidth, lon + pointWidth, lat - pointWidth, lat + pointWidth);
tree.insert(env, p);
points.add(p);
treeSet.add(p);

return true;
}
}

return false;
return concaveHullByLength(treePoints, CONCAVE_HULL_THRESHOLD);
}

/**
Expand Down Expand Up @@ -400,9 +339,9 @@ private List<Coordinate> createCoordinates(int cellId) {
return coordinates;
}

private LineString createContour(List<Coordinate> coordinates, boolean useHighDetail) {
private LineString createContour(List<Coordinate> coordinates) {
try {
Geometry geom = concHullOfNodes(coordinates, useHighDetail);
Geometry geom = concHullOfNodes(coordinates);
Polygon poly = (Polygon) geom;
poly.normalize();
return poly.getExteriorRing();
Expand All @@ -425,15 +364,6 @@ private void expandAndSaveContour(int cellId, LineString ring) {
// Add coordinates to storage, but make sure there are enough on long edges by splitting
hullLatitudes.add(ring.getPointN(i).getY());
hullLongitudes.add(ring.getPointN(i).getX());

if (i < ring.getNumPoints() - 1) {
splitEdge(ring.getPointN(i),
ring.getPointN(i + 1),
hullLatitudes,
hullLongitudes,
MIN_EDGE_LENGTH,
MAX_EDGE_LENGTH);
}
}
cellStorage.setCellContourOrder(cellId, hullLatitudes, hullLongitudes);
}
Expand All @@ -443,32 +373,6 @@ public Contour setGhStorage(GraphHopperStorage ghStorage) {
return this;
}

/**
* Splits a line between two points of the distance is longer than a limit
*
* @param point0 point0 of line
* @param point1 point1 of line
* @param latitudes latitudes to which the new coordinates are added
* @param longitudes longitudes to which th enew coordinates are added
* @param minlim limit above which the edge will be split (in meters)
* @param maxlim limit above which the edge will NOT be split anymore (in meters)
*/
private void splitEdge(Point point0, Point point1, List<Double> latitudes, List<Double> longitudes, double minlim, double maxlim) {
double lat0 = point0.getY();
double lon0 = point0.getX();
double lat1 = point1.getY();
double lon1 = point1.getX();
double dist = distance(lat0, lat1, lon0, lon1);

if (dist > minlim && dist < maxlim) {
int n = (int) Math.ceil(dist / minlim);
for (int i = 1; i < n; i++) {
latitudes.add(lat0 + i * (lat1 - lat0) / n);
longitudes.add(lon0 + i * (lon1 - lon0) / n);
}
}
}

private void splitAndAddLatLon(PointList newCoordinates, List<Coordinate> existingCoordinates, double minlim, double maxlim) {
for (int i = 0; i < newCoordinates.size() - 1; i++) {
double lat0 = newCoordinates.getLat(i);
Expand Down
Loading

0 comments on commit 9abd435

Please sign in to comment.