Commit b805dde3 authored by Matija Obreza's avatar Matija Obreza
Browse files

Country of Origin validator

parent aec11cd9
package org.genesys.geotools;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
public class CountryCLI {
// 2000 meters
private static final int ALLOWED_DISTANCE_MARGIN = 2000;
public static void main(String arg[]) throws Exception {
if (arg.length == 0) {
CountryOfOriginServiceImpl countryOfOriginService = new CountryOfOriginServiceImpl();
countryOfOriginService.afterPropertiesSet();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Pattern pattern = Pattern.compile("^(\\w{3})[\\s,]+(\\-?\\d*\\.?\\d*)[\\s,]+(\\-?\\d*\\.?\\d*)$");
System.err.println("Expects input rows in format: Latitude\tLongitude");
System.err.println("Enter 'q' to quit.");
System.out.println("OrigCty\tLatitude\tLongitude\tResult");
String input = null;
do {
input = br.readLine();
if (input == null || "q".equals(input))
break;
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
String origCty = matcher.group(1).trim();
float latitude = Float.parseFloat(matcher.group(2).replace(",", "."));
float longitude = Float.parseFloat(matcher.group(3).replace(",", "."));
System.out.println(latitude + ", " + longitude + ", "
+ countryOfOriginService.getCountries(longitude, latitude, origCty, ALLOWED_DISTANCE_MARGIN));
} else {
System.err.println("Invalid format: " + input);
}
} while (input != null && !"q".equals(input));
} else {
doCSV(arg);
}
}
private static void doCSV(String[] arg) throws IOException {
int columnOrigCty = 5;
int columnLatitude = 4;
int columnLongitude = 3;
CSVWriter writer = new CSVWriter(new OutputStreamWriter(System.out), ',', '"', '\\', "\n");
CSVReader reader = new CSVReader(new InputStreamReader(System.in), ',', '"', '\\', 0, false);
CountryOfOriginServiceImpl countryOfOriginService = new CountryOfOriginServiceImpl();
countryOfOriginService.afterPropertiesSet();
String[] nextLine;
int lineCount = 0;
while ((nextLine = reader.readNext()) != null) {
lineCount++;
if (lineCount % 1000 == 0) {
System.err.println("FYI, " + lineCount + " entries have been processed.");
}
String[] writeLine = Arrays.copyOf(nextLine, nextLine.length + 1);
try {
String origCty = nextLine[columnOrigCty].trim();
float longitude = Float.parseFloat(nextLine[columnLongitude].trim());
float latitude = Float.parseFloat(nextLine[columnLatitude].trim());
try {
String geoCountry = countryOfOriginService.getCountries(longitude, latitude, origCty,
ALLOWED_DISTANCE_MARGIN);
writeLine[writeLine.length - 1] = geoCountry;
// TODO Use origCty?
} catch (Exception e) {
writeLine[writeLine.length - 1] = e.getMessage();
}
} catch (Throwable e) {
writeLine[writeLine.length - 1] = "ERROR: " + e.getMessage();
}
writer.writeNext(writeLine);
}
writer.close();
reader.close();
}
}
package org.genesys.geotools;
public interface CountryOfOriginService {
String getCountries(float longitude, float latitude, String origCty, int allowedDistanceMargin) throws Exception;
}
package org.genesys.geotools;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.geotools.data.DataStore;
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.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
/**
* Polygons used here are derived from OSM data, © OpenStreetMap contributors
*/
public class CountryOfOriginServiceImpl implements CountryOfOriginService {
private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
private static final GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
private FeatureSource<SimpleFeatureType, SimpleFeature> sourceAdmin0;
private FeatureSource<SimpleFeatureType, SimpleFeature> sourceAdmin0X;
private LoadingCache<LonLatCacheKey, String> countryCache;
static {
try {
// Initialize stuff
} catch (Throwable e) {
// big problem
throw new RuntimeException(e);
}
}
public void afterPropertiesSet() throws MalformedURLException, IOException {
// http://www.gadm.org/version2
// Use "six dissolved layers"
DataStore dataStoreAdm0 = ShapefileUtils.openShapeFile("TM_WORLD_BORDERS-0.3.shp");
DataStore dataStoreAdm0X = ShapefileUtils.openShapeFile("gadm28_adm0.shp");
sourceAdmin0 = dataStoreAdm0.getFeatureSource(dataStoreAdm0.getTypeNames()[0]);
sourceAdmin0X =
dataStoreAdm0X.getFeatureSource(dataStoreAdm0X.getTypeNames()[0]);
countryCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(20, TimeUnit.SECONDS)
.build(new CacheLoader<LonLatCacheKey, String>() {
public String load(LonLatCacheKey key) throws Exception {
// System.err.println("Loading");
return _getCountry(key.getLongitude(), key.getLatitude(), key.getOrigCty(),
key.getAllowedDistanceMargin());
}
});
}
/*
* (non-Javadoc)
*
* @see org.genesys.geotools.LandOrSeaService#isOnLand(float, float, int)
*/
@Override
public String getCountries(float longitude, float latitude, String origCty, int allowedDistanceMargin)
throws Exception {
try {
return countryCache.get(new LonLatCacheKey(longitude, latitude, origCty, allowedDistanceMargin));
} catch (ExecutionException e) {
throw new Exception(e.getCause());
}
}
private String _getCountry(float longitude, float latitude, String origCtyISO, int allowedDistanceMargin)
throws Exception {
// System.err.println(longitude + ", " + latitude + " " + origCtyISO);
Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
String geometryPropertyName = sourceAdmin0.getSchema().getGeometryDescriptor().getLocalName();
// CoordinateReferenceSystem targetCRS =
// sourceAdmin0.getSchema().getGeometryDescriptor()
// .getCoordinateReferenceSystem();
ReferencedEnvelope bbox = new ReferencedEnvelope(longitude - 30, longitude + 30, latitude - 30, latitude + 30,
DefaultGeographicCRS.WGS84);
Filter filterExact =
// ff.and(
// ff.equal(ff.property("ISO3"), ff.literal(origCtyISO), false),
ff.and(ff.bbox(ff.property(geometryPropertyName), bbox),
ff.contains(ff.property(geometryPropertyName), ff.literal(point))
);
StringBuffer sb = new StringBuffer();
// System.err.println(1);
FeatureCollection<SimpleFeatureType, SimpleFeature> matchingFeatures = sourceAdmin0.getFeatures(filterExact);
try (FeatureIterator<SimpleFeature> features = matchingFeatures.features()) {
if (features.hasNext()) {
// System.err.println(3);
SimpleFeature feature = features.next();
// System.out.print(feature.getID());
// System.out.print(": ");
// System.out.println(feature.getDefaultGeometryProperty().getValue());
if (sb.length() > 0)
sb.append(", ");
sb.append(feature.getAttribute("ISO3"));
}
}
if (sb.length() == 0 || ! sb.toString().contains(origCtyISO)) {
// if (sb.length() > 0) sb.append(", ");
// sb.append("???");
// Filter filterBuffered = ff.intersects(ff.property(geometryPropertyName),
// ff.literal(ShapefileUtils.getPointBuffer(point, allowedDistanceMargin)));
// System.err.println(4);
FeatureCollection<SimpleFeatureType, SimpleFeature> matchingFeatures2 = sourceAdmin0X
.getFeatures(filterExact);
// System.err.println(4.1);
try (FeatureIterator<SimpleFeature> features = matchingFeatures2.features()) {
if (features.hasNext()) {
// System.err.println(7);
SimpleFeature feature = features.next();
// System.out.print(feature.getID());
// System.out.print(": ");
//
// System.out.println(feature.getDefaultGeometryProperty().getValue());
Object isoAttr = feature.getAttribute("ISO");
String countryIsoCode = isoAttr == null ? "" : isoAttr.toString();
if (!sb.toString().contains(countryIsoCode)) {
if (sb.length() > 0)
sb.append(", ");
sb.append(countryIsoCode);
}
}
}
}
// System.err.println(sb);
return sb.toString();
}
}
......@@ -7,6 +7,7 @@ public class LonLatCacheKey implements Serializable {
private static final long serialVersionUID = -3626533849742141104L;
private float longitude;
private float latitude;
private String origCty;
private int allowedDistanceMargin;
public LonLatCacheKey(float longitude, float latitude, int allowedDistanceMargin) {
......@@ -15,6 +16,14 @@ public class LonLatCacheKey implements Serializable {
this.allowedDistanceMargin = allowedDistanceMargin;
}
public LonLatCacheKey(float longitude, float latitude, String origCty, int allowedDistanceMargin) {
this.longitude = longitude;
this.latitude = latitude;
this.origCty = origCty;
this.allowedDistanceMargin = allowedDistanceMargin;
}
@Override
public int hashCode() {
final int prime = 31;
......@@ -22,6 +31,7 @@ public class LonLatCacheKey implements Serializable {
result = prime * result + allowedDistanceMargin;
result = prime * result + Float.floatToIntBits(latitude);
result = prime * result + Float.floatToIntBits(longitude);
result = prime * result + ((origCty == null) ? 0 : origCty.hashCode());
return result;
}
......@@ -40,6 +50,10 @@ public class LonLatCacheKey implements Serializable {
return false;
if (Float.floatToIntBits(longitude) != Float.floatToIntBits(other.longitude))
return false;
if (origCty == null) {
if (other.origCty != null)
return false;
} else if (!origCty.equals(other.origCty))
return false;
return true;
}
......@@ -55,4 +69,8 @@ public class LonLatCacheKey implements Serializable {
public int getAllowedDistanceMargin() {
return allowedDistanceMargin;
}
public String getOrigCty() {
return origCty;
}
}
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