Skip to content

Commit

Permalink
Merge branch 'onthegomap:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
phanecak-maptiler authored Oct 23, 2024
2 parents f9be17e + e2a1eb7 commit e1afe4c
Show file tree
Hide file tree
Showing 41 changed files with 799 additions and 274 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: onthegomap
4 changes: 2 additions & 2 deletions planetiler-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-ffi</artifactId>
<version>2.2.16</version>
<version>2.2.17</version>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
Expand Down Expand Up @@ -179,7 +179,7 @@
<dependency>
<groupId>blue.strategic.parquet</groupId>
<artifactId>parquet-floor</artifactId>
<version>1.46</version>
<version>1.47</version>
</dependency>

</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,40 @@ public Feature innermostPoint(String layer) {
return innermostPoint(layer, 0.1);
}


/**
* Starts building a new point map feature at the midpoint of this line, or the longest line segment if a
* multilinestring.
*
* @param layer the output vector tile layer this feature will be written to
* @return a feature that can be configured further.
*/
public Feature lineMidpoint(String layer) {
try {
return geometry(layer, source.lineMidpoint());
} catch (GeometryException e) {
e.log(stats, "feature_line_midpoint", "Error getting midpoint for " + source);
return empty(layer);
}
}

/**
* Starts building a new point map feature at a certain ratio along the linestring or longest segment if it is a
* multilinestring.
*
* @param layer the output vector tile layer this feature will be written to
* @param ratio the ratio along the line: 0 for start, 1 for end, 0.5 for midpoint
* @return a feature that can be configured further.
*/
public Feature pointAlongLine(String layer, double ratio) {
try {
return geometry(layer, source.pointAlongLine(ratio));
} catch (GeometryException e) {
e.log(stats, "feature_point_along_line", "Error getting point along line for " + source);
return empty(layer);
}
}

/** Returns the minimum zoom level at which this feature is at least {@code pixelSize} pixels large. */
public int getMinZoomForPixelSize(double pixelSize) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import com.onthegomap.planetiler.reader.WithTags;
import com.onthegomap.planetiler.util.Parse;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;

/**
* Destination data types for an attribute that link the type to functions that can parse the value from an input object
*/
public enum DataType implements BiFunction<WithTags, String, Object> {
public enum DataType implements TypedGetter {
GET_STRING("string", WithTags::getString, Parse::parseStringOrNull),
GET_BOOLEAN("boolean", WithTags::getBoolean, Parse::bool),
GET_DIRECTION("direction", WithTags::getDirection, Parse::direction),
Expand All @@ -17,11 +16,11 @@ public enum DataType implements BiFunction<WithTags, String, Object> {
GET_DOUBLE("double", Parse::parseDoubleOrNull),
GET_TAG("get", WithTags::getTag, s -> s);

private final BiFunction<WithTags, String, Object> getter;
private final TypedGetter getter;
private final String id;
private final UnaryOperator<Object> parser;

DataType(String id, BiFunction<WithTags, String, Object> getter, UnaryOperator<Object> parser) {
DataType(String id, TypedGetter getter, UnaryOperator<Object> parser) {
this.id = id;
this.getter = getter;
this.parser = parser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -51,15 +50,15 @@ static And and(Expression... children) {
return and(List.of(children));
}

static And and(List<Expression> children) {
static And and(List<? extends Expression> children) {
return new And(children);
}

static Or or(Expression... children) {
return or(List.of(children));
}

static Or or(List<Expression> children) {
static Or or(List<? extends Expression> children) {
return new Or(children);
}

Expand Down Expand Up @@ -91,7 +90,7 @@ static MatchAny matchAny(String field, List<?> values) {
* <p>
* {@code values} can contain exact matches, "%text%" to match any value containing "text", or "" to match any value.
*/
static MatchAny matchAnyTyped(String field, BiFunction<WithTags, String, Object> typeGetter, Object... values) {
static MatchAny matchAnyTyped(String field, TypedGetter typeGetter, Object... values) {
return matchAnyTyped(field, typeGetter, List.of(values));
}

Expand All @@ -101,8 +100,7 @@ static MatchAny matchAnyTyped(String field, BiFunction<WithTags, String, Object>
* <p>
* {@code values} can contain exact matches, "%text%" to match any value containing "text", or "" to match any value.
*/
static MatchAny matchAnyTyped(String field, BiFunction<WithTags, String, Object> typeGetter,
List<?> values) {
static MatchAny matchAnyTyped(String field, TypedGetter typeGetter, List<?> values) {
return MatchAny.from(field, typeGetter, values);
}

Expand Down Expand Up @@ -154,7 +152,7 @@ static MatchSourceLayer matchSourceLayer(String layer) {
return new MatchSourceLayer(layer);
}

private static String generateJavaCodeList(List<Expression> items) {
private static String generateJavaCodeList(List<? extends Expression> items) {
return items.stream().map(Expression::generateJavaCode).collect(Collectors.joining(", "));
}

Expand Down Expand Up @@ -269,7 +267,7 @@ public boolean evaluate(WithTags input, List<String> matchKeys) {
}
}

record And(List<Expression> children) implements Expression {
record And(List<? extends Expression> children) implements Expression {

@Override
public String generateJavaCode() {
Expand Down Expand Up @@ -307,7 +305,7 @@ public Expression simplifyOnce() {
}
}

record Or(List<Expression> children) implements Expression {
record Or(List<? extends Expression> children) implements Expression {

@Override
public String generateJavaCode() {
Expand Down Expand Up @@ -405,10 +403,10 @@ record MatchAny(
String field, List<?> values, Set<String> exactMatches,
Pattern pattern,
boolean matchWhenMissing,
BiFunction<WithTags, String, Object> valueGetter
TypedGetter valueGetter
) implements Expression {

static MatchAny from(String field, BiFunction<WithTags, String, Object> valueGetter, List<?> values) {
static MatchAny from(String field, TypedGetter valueGetter, List<?> values) {
List<String> exactMatches = new ArrayList<>();
List<String> patterns = new ArrayList<>();

Expand Down Expand Up @@ -474,6 +472,10 @@ public boolean evaluate(WithTags input, List<String> matchKeys) {

@Override
public Expression partialEvaluate(PartialInput input) {
if (field == null) {
// dynamic getters always need to be evaluated
return this;
}
Object value = input.getTag(field);
return value == null ? this : constBool(evaluate(new ArrayList<>(), value));
}
Expand All @@ -496,7 +498,9 @@ private boolean evaluate(List<String> matchKeys, Object value) {
return false;
} else {
String str = value.toString();
if (exactMatches.contains(str)) {
// when field is null, we rely on a dynamic getter function so when exactMatches is empty we match
// on any value
if (exactMatches.contains(str) || (field == null && exactMatches.isEmpty())) {
matchKeys.add(field);
return true;
}
Expand All @@ -510,7 +514,13 @@ private boolean evaluate(List<String> matchKeys, Object value) {

@Override
public Expression simplifyOnce() {
return isMatchAnything() ? matchField(field) : this;
if (isMatchAnything()) {
return matchField(field);
} else if (valueGetter instanceof Simplifiable<?> simplifiable) {
return new MatchAny(field, values, exactMatches, pattern, matchWhenMissing,
(TypedGetter) simplifiable.simplifyOnce());
}
return this;
}

@Override
Expand Down Expand Up @@ -557,6 +567,11 @@ private String patternString() {
public int hashCode() {
return Objects.hash(field, values, exactMatches, patternString(), matchWhenMissing, valueGetter);
}

public boolean mustAlwaysEvaluate() {
// when field is null we rely on a dynamic getter function
return field == null || matchWhenMissing;
}
}

/** Evaluates to true if an input element contains any value for {@code field} tag. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import static com.onthegomap.planetiler.expression.Expression.TRUE;
import static com.onthegomap.planetiler.expression.Expression.matchType;

import com.onthegomap.planetiler.reader.SourceFeature;
import com.onthegomap.planetiler.reader.WithGeometryType;
import com.onthegomap.planetiler.reader.WithSource;
import com.onthegomap.planetiler.reader.WithSourceLayer;
import com.onthegomap.planetiler.reader.WithTags;
import java.util.ArrayList;
import java.util.Comparator;
Expand Down Expand Up @@ -60,7 +61,7 @@ private static boolean mustAlwaysEvaluate(Expression expression) {
case Expression.Or(var children) -> children.stream().anyMatch(MultiExpression::mustAlwaysEvaluate);
case Expression.And(var children) -> children.stream().allMatch(MultiExpression::mustAlwaysEvaluate);
case Expression.Not(var child) -> !mustAlwaysEvaluate(child);
case Expression.MatchAny any when any.matchWhenMissing() -> true;
case Expression.MatchAny any when any.mustAlwaysEvaluate() -> true;
case null, default -> !(expression instanceof Expression.MatchAny) &&
!(expression instanceof Expression.MatchField) &&
!FALSE.equals(expression);
Expand All @@ -79,7 +80,7 @@ private static void getRelevantKeys(Expression exp, Consumer<String> acceptKey)
or.children().forEach(child -> getRelevantKeys(child, acceptKey));
} else if (exp instanceof Expression.MatchField field) {
acceptKey.accept(field.field());
} else if (exp instanceof Expression.MatchAny any && !any.matchWhenMissing()) {
} else if (exp instanceof Expression.MatchAny any && !any.mustAlwaysEvaluate()) {
acceptKey.accept(any.field());
}
// ignore not case since not(matchAny("field", "")) should track "field" as a relevant key, but that gets
Expand Down Expand Up @@ -436,7 +437,7 @@ private SourceLayerIndex(MultiExpression<T> expressions, boolean warn) {

@Override
String extract(WithTags input) {
return input instanceof SourceFeature feature ? feature.getSourceLayer() : null;
return input instanceof WithSourceLayer feature ? feature.getSourceLayer() : null;
}
}

Expand All @@ -451,7 +452,7 @@ private SourceIndex(MultiExpression<T> expressions, boolean warn) {

@Override
String extract(WithTags input) {
return input instanceof SourceFeature feature ? feature.getSource() : null;
return input instanceof WithSource feature ? feature.getSource() : null;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.onthegomap.planetiler.expression;

import com.onthegomap.planetiler.reader.WithTags;

@FunctionalInterface
public interface TypedGetter {
Object apply(WithTags withTags, String tag);
}
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,21 @@ public static int minZoomForPixelSize(double worldGeometrySize, double minPixelS
PlanetilerConfig.MAX_MAXZOOM);
}

public static LineString getLongestLine(MultiLineString multiLineString) {
LineString result = null;
double max = -1;
for (int i = 0; i < multiLineString.getNumGeometries(); i++) {
if (multiLineString.getGeometryN(i) instanceof LineString ls) {
double length = ls.getLength();
if (length > max) {
max = length;
result = ls;
}
}
}
return result;
}

public static WKBReader wkbReader() {
return new WKBReader(JTS_FACTORY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;

/**
* Utility for extracting sub-ranges of a line.
Expand All @@ -29,6 +30,22 @@ public LineSplitter(Geometry geom) {
}
}

/**
* Returns a point at {@code ratio} along this line segment where 0 is the beginning of the line and 1 is the end.
*/
public Point get(double ratio) {
if (ratio < 0d || ratio > 1d) {
throw new IllegalArgumentException("Invalid ratio: " + ratio);
}
init();
double pos = ratio * length;
var cs = line.getCoordinateSequence();
var idx = Math.max(lowerIndex(pos), 0);
MutableCoordinateSequence result = new MutableCoordinateSequence(1);
addInterpolated(result, cs, idx, pos);
return GeoUtils.JTS_FACTORY.createPoint(result);
}

/**
* Returns a partial segment of this line from {@code start} to {@code end} where 0 is the beginning of the line and 1
* is the end.
Expand All @@ -40,6 +57,24 @@ public LineString get(double start, double end) {
if (start <= 0 && end >= 1) {
return line;
}
var cs = line.getCoordinateSequence();
init();
MutableCoordinateSequence result = new MutableCoordinateSequence();

double startPos = start * length;
double endPos = end * length;
var first = floorIndex(startPos);
var last = lowerIndex(endPos);
addInterpolated(result, cs, first, startPos);
for (int i = first + 1; i <= last; i++) {
result.addPoint(cs.getX(i), cs.getY(i));
}
addInterpolated(result, cs, last, endPos);

return GeoUtils.JTS_FACTORY.createLineString(result);
}

private void init() {
var cs = line.getCoordinateSequence();
if (nodeLocations == null) {
nodeLocations = new double[cs.size()];
Expand All @@ -57,19 +92,6 @@ public LineString get(double start, double end) {
y1 = y2;
}
}
MutableCoordinateSequence result = new MutableCoordinateSequence();

double startPos = start * length;
double endPos = end * length;
var first = floorIndex(startPos);
var last = lowerIndex(endPos);
addInterpolated(result, cs, first, startPos);
for (int i = first + 1; i <= last; i++) {
result.addPoint(cs.getX(i), cs.getY(i));
}
addInterpolated(result, cs, last, endPos);

return GeoUtils.JTS_FACTORY.createLineString(result);
}

private int floorIndex(double length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
*/
public class MutableCoordinateSequence extends PackedCoordinateSequence {

private final DoubleArrayList points = new DoubleArrayList();
private final DoubleArrayList points;

public MutableCoordinateSequence() {
this(2);
}

public MutableCoordinateSequence(int size) {
super(2, 0);
points = new DoubleArrayList(2 * size);
}

/**
Expand Down
Loading

0 comments on commit e1afe4c

Please sign in to comment.