Skip to content

Commit 74a8556

Browse files
committed
GH-4221: Add six GeoSPARQL 1.1 coordinate extrema functions
1 parent 76562fc commit 74a8556

12 files changed

Lines changed: 1002 additions & 3 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:maxX. */
30+
public class MaxXFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMaxX());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:maxY. */
30+
public class MaxYFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMaxY());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:maxZ. */
30+
public class MaxZFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMaxZ());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:minX. */
30+
public class MinXFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMinX());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:minY. */
30+
public class MinYFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMinY());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;
22+
23+
import org.apache.jena.datatypes.DatatypeFormatException;
24+
import org.apache.jena.geosparql.implementation.GeometryWrapper;
25+
import org.apache.jena.sparql.expr.ExprEvalException;
26+
import org.apache.jena.sparql.expr.NodeValue;
27+
import org.apache.jena.sparql.function.FunctionBase1;
28+
29+
/** Implements geof:minZ. */
30+
public class MinZFF extends FunctionBase1 {
31+
32+
@Override
33+
public NodeValue exec(NodeValue value) {
34+
try {
35+
GeometryWrapper geometry = GeometryWrapper.extract(value);
36+
return NodeValue.makeDouble(geometry.getMinZ());
37+
} catch (DatatypeFormatException | IllegalStateException ex) {
38+
throw new ExprEvalException(ex.getMessage(), ex);
39+
}
40+
}
41+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
package org.apache.jena.geosparql.implementation;
22+
23+
import java.util.function.DoubleBinaryOperator;
24+
25+
import org.locationtech.jts.geom.CoordinateSequence;
26+
import org.locationtech.jts.geom.CoordinateSequenceFilter;
27+
import org.locationtech.jts.geom.Geometry;
28+
29+
/**
30+
* Finds coordinate extrema in the input geometry's SRS axis order.
31+
* X and Y denote the first and second SRS dimensions, respectively.
32+
*/
33+
final class GeometryCoordinateExtrema {
34+
private GeometryCoordinateExtrema() {
35+
}
36+
37+
static double maxX(GeometryWrapper geometry) {
38+
return extreme(geometry, 0, Math::max);
39+
}
40+
41+
static double maxY(GeometryWrapper geometry) {
42+
return extreme(geometry, 1, Math::max);
43+
}
44+
45+
static double maxZ(GeometryWrapper geometry) {
46+
return zExtreme(geometry, Math::max);
47+
}
48+
49+
static double minX(GeometryWrapper geometry) {
50+
return extreme(geometry, 0, Math::min);
51+
}
52+
53+
static double minY(GeometryWrapper geometry) {
54+
return extreme(geometry, 1, Math::min);
55+
}
56+
57+
static double minZ(GeometryWrapper geometry) {
58+
return zExtreme(geometry, Math::min);
59+
}
60+
61+
private static double extreme(GeometryWrapper geometry, int ordinate,
62+
DoubleBinaryOperator accumulator) {
63+
return extreme(geometry.getParsingGeometry(), ordinate, accumulator, false);
64+
}
65+
66+
private static double zExtreme(GeometryWrapper geometry, DoubleBinaryOperator accumulator) {
67+
return extreme(geometry.getParsingGeometry(), 2, accumulator, true);
68+
}
69+
70+
private static double extreme(Geometry geometry, int ordinate,
71+
DoubleBinaryOperator accumulator, boolean skipNaN) {
72+
ExtremaFilter filter = new ExtremaFilter(ordinate, accumulator, skipNaN);
73+
geometry.apply(filter);
74+
return filter.result();
75+
}
76+
77+
private static final class ExtremaFilter implements CoordinateSequenceFilter {
78+
private final int ordinate;
79+
private final DoubleBinaryOperator accumulator;
80+
private final boolean skipNaN;
81+
private double result;
82+
private boolean found;
83+
84+
private ExtremaFilter(int ordinate, DoubleBinaryOperator accumulator, boolean skipNaN) {
85+
this.ordinate = ordinate;
86+
this.accumulator = accumulator;
87+
this.skipNaN = skipNaN;
88+
}
89+
90+
@Override
91+
public void filter(CoordinateSequence sequence, int index) {
92+
if (skipNaN && !sequence.hasZ()) {
93+
return;
94+
}
95+
double value = skipNaN ? sequence.getZ(index) : sequence.getOrdinate(index, ordinate);
96+
if (Double.isInfinite(value) || (!skipNaN && Double.isNaN(value))) {
97+
throw new IllegalStateException("Geometry has a non-finite ordinate at ordinate index " + ordinate);
98+
}
99+
if (skipNaN && Double.isNaN(value)) {
100+
return;
101+
}
102+
result = found ? accumulator.applyAsDouble(result, value) : value;
103+
found = true;
104+
}
105+
106+
@Override
107+
public boolean isDone() {
108+
return false;
109+
}
110+
111+
@Override
112+
public boolean isGeometryChanged() {
113+
return false;
114+
}
115+
116+
private double result() {
117+
if (!found) {
118+
throw new IllegalStateException("Geometry has no eligible ordinate");
119+
}
120+
return result;
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)