Commit 10d11156 authored by Matija Obreza's avatar Matija Obreza

Merge branch '5-include-distance-from-country' into 'master'

Resolve "Include distance from country"

Closes #5

See merge request !2
parents f5469f9a f693ca5b
Pipeline #5204 passed with stage
in 1 minute and 14 seconds
......@@ -33,4 +33,15 @@ public interface CountryOfOriginService {
*/
String getCountries(float longitude, float latitude, String origCty, int allowedDistanceMargin) throws Exception;
/**
* Get distance from georeference to closest point on border of specified country
*
* @param longitude the longitude
* @param latitude the latitude
* @param origCty the country ISO code
* @return distance to closest point on country border or -1 if not found
* @throws Exception
*/
double distanceToBorder(float longitude, float latitude, String origCty) throws Exception;
}
......@@ -22,6 +22,7 @@ import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.genesys.geotools.service.CountryOfOriginService;
import org.genesys.geotools.service.LonLatCacheKey;
......@@ -31,8 +32,10 @@ import org.geotools.data.FeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.GeodeticCalculator;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
......@@ -47,7 +50,9 @@ import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.operation.distance.DistanceOp;
/**
* Polygons used here are derived from OSM data, © OpenStreetMap contributors.
......@@ -72,6 +77,9 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
/** The country cache. */
private LoadingCache<LonLatCacheKey, String> countryCache;
/** The country cache. */
private LoadingCache<LonLatCacheKey, Double> countryDistanceCache;
/** The debug. */
private final boolean debug = false;
......@@ -80,7 +88,7 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
public void setDataFolderPath(String dataFolderPath) {
this.dataFolderPath = dataFolderPath;
}
static {
try {
// Initialize stuff
......@@ -106,14 +114,21 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
sourceAdmin0 = dataStoreAdm0.getFeatureSource(dataStoreAdm0.getTypeNames()[0]);
sourceAdmin0X = dataStoreAdm0X.getFeatureSource(dataStoreAdm0X.getTypeNames()[0]);
countryCache = CacheBuilder.newBuilder().maximumSize(5000).recordStats().expireAfterWrite(20, TimeUnit.SECONDS).build(
new CacheLoader<LonLatCacheKey, String>() {
@Override
public String load(final LonLatCacheKey key) throws Exception {
// LOG.debug("Loading");
return _getCountry(key.getLongitude(), key.getLatitude(), key.getOrigCty(), key.getAllowedDistanceMargin());
}
});
countryCache = CacheBuilder.newBuilder().maximumSize(5000).recordStats().expireAfterWrite(20, TimeUnit.SECONDS).build(new CacheLoader<LonLatCacheKey, String>() {
@Override
public String load(final LonLatCacheKey key) throws Exception {
// LOG.debug("Loading");
return _getCountry(key.getLongitude(), key.getLatitude(), key.getOrigCty(), key.getAllowedDistanceMargin());
}
});
countryDistanceCache = CacheBuilder.newBuilder().maximumSize(5000).recordStats().expireAfterWrite(20, TimeUnit.SECONDS).build(new CacheLoader<LonLatCacheKey, Double>() {
@Override
public Double load(final LonLatCacheKey key) throws Exception {
// LOG.debug("Loading");
return _distanceToBorder(key.getLongitude(), key.getLatitude(), key.getOrigCty());
}
});
}
/*
......@@ -121,8 +136,7 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
* @see org.genesys.geotools.LandOrSeaService#isOnLand(float, float, int)
*/
@Override
public String getCountries(final float longitude, final float latitude, final String origCty, final int allowedDistanceMargin)
throws Exception {
public String getCountries(final float longitude, final float latitude, final String origCty, final int allowedDistanceMargin) throws Exception {
// 1 geographical mile is 1855.3248 metres for WGS84
// 1855.3248m * 60 = 111319.488m
......@@ -149,8 +163,7 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
* @return the string
* @throws Exception the exception
*/
private String _getCountry(final float longitude, final float latitude, final String origCtyISO, final int allowedDistanceMargin)
throws Exception {
private String _getCountry(final float longitude, final float latitude, final String origCtyISO, final int allowedDistanceMargin) throws Exception {
// LOG.debug(longitude + ", " + latitude + " " + origCtyISO);
final StopWatch stopWatch = new StopWatch();
......@@ -161,8 +174,7 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
// sourceAdmin0.getSchema().getGeometryDescriptor()
// .getCoordinateReferenceSystem();
final ReferencedEnvelope bbox = new ReferencedEnvelope(longitude - 30, longitude + 30, latitude - 30, latitude + 30,
DefaultGeographicCRS.WGS84);
final ReferencedEnvelope bbox = new ReferencedEnvelope(longitude - 30, longitude + 30, latitude - 30, latitude + 30, DefaultGeographicCRS.WGS84);
final Filter filterExact =
// ff.and(
......@@ -193,7 +205,7 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
LOG.debug("Processing time split: " + processingTime);
}
if ((sb.length() == 0) || !sb.toString().contains(origCtyISO)) {
if ((sb.length() == 0) || StringUtils.trimToNull(origCtyISO) == null || !sb.toString().contains(origCtyISO)) {
// if (sb.length() > 0) sb.append(", ");
// sb.append("???");
// Filter filterBuffered =
......@@ -233,14 +245,63 @@ public class CountryOfOriginServiceImpl implements CountryOfOriginService {
return sb.toString();
}
@Override
public double distanceToBorder(float longitude, float latitude, String origCty) throws Exception {
try {
return countryDistanceCache.get(new LonLatCacheKey(longitude, latitude, origCty, 0));
} catch (final ExecutionException e) {
throw new Exception(e.getCause());
}
}
private double _distanceToBorder(float longitude, float latitude, String origCty) throws Exception {
Coordinate coordinate = new Coordinate(longitude, latitude);
final Point point = geometryFactory.createPoint(coordinate);
final Filter filterExact = ff.equal(ff.property("ISO3"), ff.literal(origCty), false);
LOG.trace("Distance of {} to {}", coordinate, origCty);
try {
final FeatureCollection<SimpleFeatureType, SimpleFeature> matchingFeatures = sourceAdmin0.getFeatures(filterExact);
if (matchingFeatures.size() > 0) {
if (LOG.isTraceEnabled()) {
try (FeatureIterator<SimpleFeature> features = matchingFeatures.features()) {
while (features.hasNext()) {
final SimpleFeature feature = features.next();
LOG.trace("{}: {} Attrs={}", feature.getID(), feature.getDefaultGeometryProperty().getValue(), feature.getAttributes());
}
}
}
try (FeatureIterator<SimpleFeature> features = matchingFeatures.features()) {
if (features.hasNext()) {
final SimpleFeature feature = features.next();
MultiPolygon geometry = (MultiPolygon) feature.getDefaultGeometry();
GeodeticCalculator gc = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
gc.setStartingPosition(JTS.toDirectPosition(DistanceOp.nearestPoints(geometry, point)[0], DefaultGeographicCRS.WGS84));
gc.setDestinationPosition(JTS.toDirectPosition(coordinate, DefaultGeographicCRS.WGS84));
return gc.getOrthodromicDistance();
} else {
return -1.0d;
}
}
} else {
LOG.debug("No geometry with ISO={}", origCty);
return -1.0d;
}
} catch (IOException e) {
throw e;
}
}
/**
* Prints the cache.
*/
public void printCache() {
final CacheStats stats = countryCache.stats();
if (LOG.isInfoEnabled()) {
LOG.info("Hit count=" + stats.hitCount() + " rate=" + stats.hitRate() + " Miss count=" + stats.missCount() + " rate=" + stats
.missRate());
LOG.info("Hit count=" + stats.hitCount() + " rate=" + stats.hitRate() + " Miss count=" + stats.missCount() + " rate=" + stats.missRate());
}
}
}
/*
* Copyright 2016 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.geotools.cli;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.genesys.geotools.service.impl.CountryOfOriginServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CountryDistance {
private final static Logger LOG = LoggerFactory.getLogger(CountryDistance.class);
public static void main(final String arg[]) throws Exception {
if (arg.length == 0) {
final CountryOfOriginServiceImpl countryOfOriginService = new CountryOfOriginServiceImpl();
countryOfOriginService.afterPropertiesSet();
final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
final Pattern pattern = Pattern.compile("^(\\w{3})[\\s,]+(\\-?\\d*\\.?\\d*)[\\s,]+(\\-?\\d*\\.?\\d*)$");
LOG.info("Expects input rows in format: ORIGCTY\tDECLONGITUDE\tDECLATITUDE");
LOG.info("Enter 'q' to quit.");
System.out.println("ORIGCTY\tDECLONGITUDE\tDECLATITUDE\tRESULT");
String input = null;
do {
input = br.readLine();
if ((input == null) || "q".equals(input))
break;
final Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
final String origCty = matcher.group(1).trim();
final float longitude = Float.parseFloat(matcher.group(2).replace(",", "."));
final float latitude = Float.parseFloat(matcher.group(3).replace(",", "."));
System.out.println(origCty + "\t" + longitude + "\t" + latitude + "\t" + countryOfOriginService.distanceToBorder(longitude, latitude, origCty));
} else {
LOG.info("Invalid format: " + input);
}
} while ((input != null) && !"q".equals(input));
} else {
throw new RuntimeException("Nono.");
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment