Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiles are empty on the Western U.S. #80

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*.log
*.iml
**idea/
/.metadata/
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package mil.nga.giat.data.elasticsearch;

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.elasticsearch.client.RestClient;
Expand Down Expand Up @@ -52,6 +53,8 @@ public class ElasticDataStoreFactory implements DataStoreFactorySpi {

public static final Param SCROLL_SIZE = new Param("scroll_size", Long.class, "Scroll size (ignored if scroll_enabled=false)", false, 20);

public static final Param CONNECTION_TIMEOUT_MILISECONDS = new Param("connection_timeout", Integer.class, "ElasticSearch connection timeout (default 5000 miliseconds)", false, 5000);

public static final Param SCROLL_TIME_SECONDS = new Param("scroll_time", Integer.class, "Time to keep the scroll open in seconds (ignored if scroll_enabled=false)", false, 120);

public static final Param ARRAY_ENCODING = new Param("array_encoding", String.class, "Array encoding strategy. Allowed values are \"JSON\" (keep arrays) "
Expand All @@ -72,6 +75,7 @@ public class ElasticDataStoreFactory implements DataStoreFactorySpi {
SOURCE_FILTERING_ENABLED,
SCROLL_ENABLED,
SCROLL_SIZE,
CONNECTION_TIMEOUT_MILISECONDS,
SCROLL_TIME_SECONDS,
ARRAY_ENCODING,
GRID_SIZE,
Expand Down Expand Up @@ -132,10 +136,17 @@ public DataStore createDataStore(Map<String, Serializable> params) throws IOExce
final String arrayEncoding = (String) getValue(ARRAY_ENCODING, params);
final Boolean sslEnabled = (Boolean) getValue(SSL_ENABLED, params);
final Boolean sslRejectUnauthorized = (Boolean) getValue(SSL_REJECT_UNAUTHORIZED, params);
final Integer connectionTimeout = (Integer) getValue(CONNECTION_TIMEOUT_MILISECONDS, params);

final String scheme = sslEnabled ? "https" : "http";
final RestClientBuilder builder = RestClient.builder(new HttpHost(searchHost, hostPort, scheme));

final String scheme = sslEnabled ? "https" : "http";
final RestClientBuilder builder = RestClient.builder(new HttpHost(searchHost, hostPort, scheme)).setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public Builder customizeRequestConfig(Builder requestConfigBuilder) {
return requestConfigBuilder.setConnectTimeout(connectionTimeout);
}
});

if (sslEnabled) {
builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateFilter;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryComponentFilter;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

class FilterToElasticHelper {
Expand Down Expand Up @@ -288,31 +289,93 @@ boolean isCurrentGeography() {
}

protected Literal clipToWorld(Literal geometry) {
if(geometry != null) {
if (geometry != null) {
Geometry g = geometry.evaluate(null, Geometry.class);
if(g != null) {
g.apply(new GeometryComponentFilter() {
@Override
public void filter(Geometry geom) {
geom.apply(new CoordinateFilter() {
@Override
public void filter(Coordinate coord) {
coord.setCoordinate(new Coordinate(clipLon(coord.x),clipLat(coord.y)));
if (g != null) {
Envelope env = g.getEnvelopeInternal();
// first, limit to world
if (!WORLD.contains(env)) {
g = sanitizePolygons(g.intersection(JTS.toGeometry(WORLD)));
}

// second, elasticsearch will always use the shortest distance between two
// points, if an arc is longer than 180 degrees the opposite will
// be used instead, so we have to slice the geometry in parts
env = g.getEnvelopeInternal();
if (Math.sqrt(env.getWidth() * env.getWidth() + env.getHeight() * env.getHeight())
>= 180) {
// slice in 90x90 degrees quadrants, none of them has a diagonal longer than 180
final List<Polygon> polygons = new ArrayList<Polygon>();
for (double lon = Math.floor(env.getMinX()); lon < env.getMaxX(); lon += 90) {
for (double lat = Math.floor(env.getMinY());
lat < env.getMaxY();
lat += 90) {
Geometry quadrant =
JTS.toGeometry(new Envelope(lon, lon + 90, lat, lat + 90));
Geometry cut = sanitizePolygons(g.intersection(quadrant));
if (!cut.isEmpty()) {
if (cut instanceof Polygon) {
polygons.add((Polygon) cut);
} else {
for (int i = 0; i < cut.getNumGeometries(); i++) {
polygons.add((Polygon) cut.getGeometryN(i));
}
}
}
});
}
}
});
geometry = CommonFactoryFinder.getFilterFactory(null).literal(g);
g = toPolygon(g.getFactory(), polygons);
}

geometry = CommonFactoryFinder.getFilterFactory(null).literal(g);
}
}

return geometry;
}

/**
* Given a geometry that might contain heterogeneous components extracts only the polygonal ones
*
* @param geometry
* @return
*/
private Geometry sanitizePolygons(Geometry geometry) {
// already sane?
if (geometry == null || geometry instanceof Polygon || geometry instanceof MultiPolygon) {
return geometry;
}

// filter out only polygonal parts
final List<Polygon> polygons = new ArrayList<Polygon>();
geometry.apply(
new GeometryComponentFilter() {

public void filter(Geometry geom) {
if (geom instanceof Polygon) {
polygons.add((Polygon) geom);
}
}
});

// turn filtered selection into a geometry
return toPolygon(geometry.getFactory(), polygons);
}

private Geometry toPolygon(GeometryFactory gf, final List<Polygon> polygons) {
if (polygons.size() == 0) {
return gf.createGeometryCollection(null);
} else if (polygons.size() == 1) {
return polygons.get(0);
} else {
return gf.createMultiPolygon(
(Polygon[]) polygons.toArray(new Polygon[polygons.size()]));
}
}

protected double clipLon(double lon) {
double x = Math.signum(lon)*(Math.abs(lon)%360);
return x = x>180 ? x-360 : (x<-180 ? x+360 : x);
return x>180 ? x-360 : (x<-180 ? x+360 : x);
}

protected double clipLat(double lat) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,16 @@ public void testBBOXCoveringDateline() throws Exception {
FilterFactory ff = dataStore.getFilterFactory();
BBOX bbox = ff.bbox("geo", 178, -98, 182, 98, "EPSG:" + SOURCE_SRID);
SimpleFeatureCollection features = featureSource.getFeatures(bbox);
assertEquals(2, features.size());
assertEquals(1, features.size());
}

@Test
public void testBBOXCoveringDatelineOnWestCoastOfUSA() throws Exception {
init("not-active","geo");
FilterFactory ff = dataStore.getFilterFactory();
BBOX bbox = ff.bbox("geo", -180, 0, -90, 66, "EPSG:" + SOURCE_SRID);
SimpleFeatureCollection features = featureSource.getFeatures(bbox);
assertEquals(1, features.size());
}

@Test
Expand All @@ -317,7 +326,7 @@ public void testBBOXBeyondDateline() throws Exception {
FilterFactory ff = dataStore.getFilterFactory();
BBOX bbox = ff.bbox("geo", 180.5, -98, 182, 98, "EPSG:" + SOURCE_SRID);
SimpleFeatureCollection features = featureSource.getFeatures(bbox);
assertEquals(1, features.size());
assertEquals(0, features.size());
}

@Test
Expand Down