Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@
<artifactId>mockserver-client-java</artifactId>
<version>5.15.0</version>
</dependency>
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.8</version>
</dependency>
</dependencies>
</dependencyManagement>
<distributionManagement>
Expand Down
4 changes: 4 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
</dependency>
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,20 @@ public RestClientTransport restClientTransport() {
public ElasticsearchClient geoNetworkElasticsearchClient(RestClientTransport transport) {
return new ElasticsearchClient(transport);
}

/**
* The elastic search client to do the query
* @param client - The elastic search client
* @param mapper - Object mapper for string to object transformation
* @param indexName - The elastic index name that store the STAC from es-indexer
* @param pageSize - Do not set this value too high, say 5000 will crash elastic search
* @param searchAsYouTypeSize - The number of search result return for search as you type
* @return
*/
@Bean
public Search createElasticSearch(ElasticsearchClient client,
ObjectMapper mapper,
@Value("${elasticsearch.index.name}") String indexName,
@Value("${elasticsearch.index.pageSize:5000}") Integer pageSize,
@Value("${elasticsearch.index.pageSize:2500}") Integer pageSize,
@Value("${elasticsearch.search_as_you_type.size:10}") Integer searchAsYouTypeSize) {

return new ElasticSearch(client, mapper, indexName, pageSize, searchAsYouTypeSize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ default <D extends StacCollectionModel> Collection getCollection(D m, Filter fil
if(m.getSummaries() != null ) {
Map<?, ?> noLand = m.getSummaries().getGeometryNoLand();
if (noLand != null) {
// Geometry from elastic search always store in EPSG4326
GeometryUtils.readGeometry(noLand)
.ifPresent(input -> {
Geometry g = filter != null ? (Geometry)filter.accept(visitor, input) : input;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ public static CQLCrsType convertFromUrl(String url) {

public static Geometry transformGeometry(Geometry geometry, CQLCrsType source, CQLCrsType target) throws FactoryException, TransformException {

GeometryFactory factory = JTSFactoryFinder.getGeometryFactory();

CoordinateReferenceSystem sourceCRS = CRS.decode(source.code);
CoordinateReferenceSystem targetCRS = CRS.decode(target.code);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.geotools.filter.visitor.DefaultFilterVisitor;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.*;
import org.opengis.filter.spatial.Intersects;

@Slf4j
Expand All @@ -15,13 +13,11 @@ public class GeometryVisitor extends DefaultFilterVisitor {
@Override
public Object visit(Intersects filter, Object data) {
if(filter instanceof IntersectsImpl<?> impl) {
if(impl.getPreparedGeometry().isPresent()) {
if(impl.getGeometry().isPresent()) {
if (data instanceof Polygon || data instanceof GeometryCollection) {
// To handle minor precision issues, try applying a small buffer (like 0.0) to clean up
// minor topology errors. This is a trick commonly used with JTS
return impl.getPreparedGeometry().get()
.getGeometry()
.intersection(((Geometry) data).buffer(0.0));
return impl.getGeometry().get().intersection(((Geometry) data).buffer(0.0));
}
else {
return data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
import org.geotools.filter.AttributeExpressionImpl;
import org.geotools.filter.LiteralExpressionImpl;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.prep.PreparedGeometry;
import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.spatial.Intersects;
Expand All @@ -28,7 +25,7 @@ public class IntersectsImpl<T extends Enum<T> & CQLFieldsInterface> implements I
protected Expression expression2;

@Getter
protected Optional<PreparedGeometry> preparedGeometry = Optional.empty();
protected Optional<Geometry> geometry = Optional.empty();

public IntersectsImpl(Expression expression1, Expression expression2, CQLCrsType cqlCrsType) {
if(expression1 instanceof AttributeExpressionImpl attribute && expression2 instanceof LiteralExpressionImpl literal) {
Expand All @@ -37,7 +34,9 @@ public IntersectsImpl(Expression expression1, Expression expression2, CQLCrsType

try {
String geojson = GeometryUtils.convertToGeoJson(literal, cqlCrsType);
preparedGeometry = GeometryUtils.readGeometry(geojson).map(g -> PreparedGeometryFactory.prepare(g));
geometry = GeometryUtils
.readGeometry(geojson)
.map(g -> GeometryUtils.normalizePolygon(g));
}
catch(Exception ex) {
logger.warn("Exception in parsing, query result will be wrong", ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.shape.jts.JtsGeometry;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
Expand All @@ -25,7 +27,6 @@
public class GeometryUtils {

protected static final int PRECISION = 15;

protected static GeometryFactory factory = new GeometryFactory(new PrecisionModel(), 4326);

protected static ObjectMapper mapper = new ObjectMapper();
Expand Down Expand Up @@ -208,4 +209,15 @@ public static Optional<Geometry> readGeometry(Object input) {
return Optional.empty();
}
}
/**
* Normalize a polygon by adjusting longitudes to the range [-180, 180], and return both parts as a GeometryCollection.
*
* @param polygon The input polygon.
* @return A polygon / multi-polygon unwrap at dateline.
*/
public static Geometry normalizePolygon(Geometry polygon) {
// Set dateline 180 check to true to unwrap a polygon across -180 line
JtsGeometry jtsGeometry = new JtsGeometry(polygon, JtsSpatialContext.GEO, true, false);
return jtsGeometry.getGeom();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.io.ParseException;
import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
Expand Down Expand Up @@ -54,7 +55,7 @@ public static void init() {
* @throws ParseException - Will not throw
*/
@Test
public void verifyIntersectionWorks() throws CQLException, IOException, FactoryException, TransformException, ParseException {
public void verifyIntersectionWorks1() throws CQLException, IOException, FactoryException, TransformException, ParseException {
// Assume we have the following CQL
Converter.Param param = Converter.Param.builder()
.coordinationSystem(CQLCrsType.EPSG4326)
Expand Down Expand Up @@ -85,4 +86,43 @@ public void verifyIntersectionWorks() throws CQLException, IOException, FactoryE
Assertions.assertTrue(expected.isPresent(), "Expected parse correct");
Assertions.assertEquals(g, expected.get(), "They are equals");
}

/**
* Test case where POLYGON cross the -180 line, we should be able to handle it correctly.
* the parser will split the polygon into two and then apply the intersection with the noloand in json sample
* it will result in a single polygon and therefore we can calculate the centroid
*
* @throws CQLException - Will not throw
* @throws IOException - Will not throw
* @throws FactoryException - Will not throw
* @throws TransformException - Will not throw
* @throws ParseException - Will not throw
*/
@Test
public void verifyIntersectionWorks2() throws CQLException, IOException, FactoryException, TransformException, ParseException {

// Parse the json and get the noland section
String json = BaseTestClass.readResourceFile("classpath:databag/0015db7e-e684-7548-e053-08114f8cd4ad.json");
StacCollectionModel model = mapper.readValue(json, StacCollectionModel.class);

Filter filter = CompilerUtil.parseFilter(
Language.CQL,
"score>=1.5 AND INTERSECTS(geometry,POLYGON ((-203.16603491348164 -60.248194404495756, -86.85117538227594 -60.248194404495756, -86.85117538227594 15.902738674628525, -203.16603491348164 15.902738674628525, -203.16603491348164 -60.248194404495756)))",
factory);

Optional<Geometry> geo = GeometryUtils.readGeometry(model.getSummaries().getGeometryNoLand());

Assertions.assertTrue(geo.isPresent(), "Parse no land correct");
GeometryVisitor visitor = GeometryVisitor.builder()
.build();

// return value are geo applied the CQL, and in this case only INTERSECTS
Geometry g = (Geometry)filter.accept(visitor, geo.get());

Assertions.assertFalse(g.isEmpty());
Assertions.assertTrue(g instanceof Polygon);

Assertions.assertEquals(g.getCentroid().getX(), 168.30090846621448, "getX()");
Assertions.assertEquals(g.getCentroid().getY(), -33.95984804960966, "getY()");
}
}
Loading
Loading