mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-07 11:32:16 +00:00
Merge branch 'master' into 'gwt25'
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortedCellTableWithStylableHeaders.java
This commit is contained in:
commit
c393031c41
2041 files changed
+139420
-115508
No files matched your search
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.geocoding</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.geocoding</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,8 +1,8 @@
|
||||
#Thu Jan 19 09:54:42 CET 2012
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
#Thu Jan 19 09:54:42 CET 2012
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
@@ -1,12 +1,12 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Geocoding
|
||||
Bundle-SymbolicName: com.sap.sailing.geocoding
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: com.sap.sailing.geocoding,
|
||||
com.sap.sailing.geocoding.impl;x-friends:="com.sap.sailing.geocoding.test"
|
||||
Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
com.sap.sailing.domain.common
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Geocoding
|
||||
Bundle-SymbolicName: com.sap.sailing.geocoding
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: com.sap.sailing.geocoding,
|
||||
com.sap.sailing.geocoding.impl;x-friends:="com.sap.sailing.geocoding.test"
|
||||
Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
com.sap.sailing.domain.common
|
||||
@@ -1,4 +1,4 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>com.sap.sailing.geocoding</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>com.sap.sailing.geocoding</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
@@ -1,69 +1,69 @@
|
||||
package com.sap.sailing.geocoding;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.Placemark;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.geocoding.impl.ReverseGeocoderImpl;
|
||||
|
||||
public interface ReverseGeocoder {
|
||||
final ReverseGeocoder INSTANCE = new ReverseGeocoderImpl();
|
||||
final double POSITION_CACHE_DISTANCE_LIMIT_IN_KM = 5.0;
|
||||
|
||||
/**
|
||||
* Returns the nearest {@link Placemark} towards the given {@link Position}.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @return The nearest {@link Placemark} towards the given {@link Position} or null if there is none within 300km
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkNearest(Position position) throws IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Placemark Placemarks} near the given {@link Position}.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @return A list of {@link Placemark Placemarks} near the given {@link Position} or null if there are none within 300km
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Searches for {@link Placemark Placemarks} near the given {@link Position} via
|
||||
* {@link ReverseGeocoder#getPlacemarkNearest(double, double) getPlacemarkNearest} and sorts them with
|
||||
* <code>comp</code>. Returns the last element in the sorted list.<br /><br />
|
||||
* The interface {@link Placemark} contains some Comparators for Placemarks.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @param comp Sorts the list
|
||||
* @return The last element in the sorted list
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkLast(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException;
|
||||
|
||||
/**
|
||||
* Searches for {@link Placemark Placemarks} near the given {@link Position} via
|
||||
* {@link ReverseGeocoder#getPlacemarkNearest(double, double) getPlacemarkNearest} and sorts them with
|
||||
* <code>comp</code>. Returns the first element in the sorted list.<br /><br />
|
||||
* The interface {@link Placemark} contains some Comparators for Placemarks.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @param comp Sorts the list
|
||||
* @return The first element in the sorted list
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkFirst(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException;
|
||||
}
|
||||
package com.sap.sailing.geocoding;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.Placemark;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.geocoding.impl.ReverseGeocoderImpl;
|
||||
|
||||
public interface ReverseGeocoder {
|
||||
final ReverseGeocoder INSTANCE = new ReverseGeocoderImpl();
|
||||
final double POSITION_CACHE_DISTANCE_LIMIT_IN_KM = 5.0;
|
||||
|
||||
/**
|
||||
* Returns the nearest {@link Placemark} towards the given {@link Position}.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @return The nearest {@link Placemark} towards the given {@link Position} or null if there is none within 300km
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkNearest(Position position) throws IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Placemark Placemarks} near the given {@link Position}.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @return A list of {@link Placemark Placemarks} near the given {@link Position} or null if there are none within 300km
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Searches for {@link Placemark Placemarks} near the given {@link Position} via
|
||||
* {@link ReverseGeocoder#getPlacemarkNearest(double, double) getPlacemarkNearest} and sorts them with
|
||||
* <code>comp</code>. Returns the last element in the sorted list.<br /><br />
|
||||
* The interface {@link Placemark} contains some Comparators for Placemarks.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @param comp Sorts the list
|
||||
* @return The last element in the sorted list
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkLast(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException;
|
||||
|
||||
/**
|
||||
* Searches for {@link Placemark Placemarks} near the given {@link Position} via
|
||||
* {@link ReverseGeocoder#getPlacemarkNearest(double, double) getPlacemarkNearest} and sorts them with
|
||||
* <code>comp</code>. Returns the first element in the sorted list.<br /><br />
|
||||
* The interface {@link Placemark} contains some Comparators for Placemarks.
|
||||
*
|
||||
* @param position The position where to search
|
||||
* @param radius The search radius
|
||||
* @param comp Sorts the list
|
||||
* @return The first element in the sorted list
|
||||
* @throws IOException
|
||||
* @throws ParseException
|
||||
*/
|
||||
Placemark getPlacemarkFirst(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException;
|
||||
}
|
||||
+274
-274
@@ -1,274 +1,274 @@
|
||||
package com.sap.sailing.geocoding.impl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.Placemark;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SerializablePosition;
|
||||
import com.sap.sailing.domain.common.impl.PlacemarkImpl;
|
||||
import com.sap.sailing.domain.common.impl.SerializablePositionImpl;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
import com.sap.sailing.domain.common.quadtree.QuadTree;
|
||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
|
||||
public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
private final String NEARBY_PLACE_SERVICE = "http://api.geonames.org/findNearbyPlaceNameJSON?";
|
||||
/**
|
||||
* Maximal distance in degree for the cache.<br />
|
||||
* The first number is the distance in kilometers and the second number is a needed calculation factor and mustn't
|
||||
* be changed! To change the distance just change the first number!
|
||||
*/
|
||||
private final double POSITION_CACHE_DISTANCE_LIMIT = ReverseGeocoder.POSITION_CACHE_DISTANCE_LIMIT_IN_KM * 0.00899928005759539236861051115911;
|
||||
private final int XKM_RADIUS = 5;
|
||||
private final int ROWS_PER_XKM_RADIUS = 15;
|
||||
|
||||
private final int MAX_ROW_NUMBER = 1000;
|
||||
private final int MAX_RADIUS = 300;
|
||||
|
||||
private QuadTree<Triple<Position, Double, List<Placemark>>> cache = new QuadTree<Triple<Position,Double,List<Placemark>>>();;
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkNearest(Position position) throws IOException, ParseException {
|
||||
Placemark p = null;
|
||||
Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getC() != null && !cachedPlacemarks.getC().isEmpty()) {
|
||||
p = cachedPlacemarks.getC().get(0);
|
||||
} else {
|
||||
JSONArray geonames = callNearestService(position);
|
||||
if (geonames != null && !geonames.isEmpty()) {
|
||||
p = JSONToPlacemark((JSONObject) geonames.get(0));
|
||||
|
||||
if (p != null) {
|
||||
List<Placemark> placemarks = new ArrayList<Placemark>();
|
||||
placemarks.add(p);
|
||||
cachePlacemarks(position, 0.0, placemarks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException {
|
||||
List<Placemark> placemarks = null;
|
||||
Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
//Calculating the search radius and the maximum number of returning Placemarks
|
||||
double limitedRadius = Math.min(radius, MAX_RADIUS);
|
||||
int radiusInt = (int) limitedRadius;
|
||||
int xKmRadius = radiusInt / XKM_RADIUS;
|
||||
int maxRows = (int) (ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius));
|
||||
maxRows = Math.min(maxRows, MAX_ROW_NUMBER);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getB() >= limitedRadius) {
|
||||
if (cachedPlacemarks.getC().size() > maxRows) {
|
||||
placemarks = cachedPlacemarks.getC().subList(0, maxRows);
|
||||
} else {
|
||||
placemarks = new ArrayList<Placemark>(cachedPlacemarks.getC());
|
||||
}
|
||||
} else {
|
||||
//Recalculating the radius and resetting the search position to keep the cache correct
|
||||
Position searchPosition = null;
|
||||
if (cachedPlacemarks != null) {
|
||||
searchPosition = cachedPlacemarks.getA();
|
||||
double distance = position.getDistance(searchPosition).getKilometers();
|
||||
limitedRadius = Math.min(MAX_RADIUS, limitedRadius + distance);
|
||||
} else {
|
||||
searchPosition = position;
|
||||
}
|
||||
|
||||
JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows);
|
||||
if (geonames != null) {
|
||||
Iterator<Object> iterator = geonames.iterator();
|
||||
placemarks = iterator.hasNext() ? new ArrayList<Placemark>() : null;
|
||||
while (iterator.hasNext()) {
|
||||
JSONObject object = (JSONObject) iterator.next();
|
||||
Placemark place = JSONToPlacemark(object);
|
||||
if (place != null) {
|
||||
placemarks.add(JSONToPlacemark(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no cached placemarks for the requested Position just cache them, otherwise update the cache
|
||||
if (cachedPlacemarks == null && placemarks != null) {
|
||||
cachePlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
} else if (placemarks != null) {
|
||||
updateCachedPlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
}
|
||||
}
|
||||
|
||||
return placemarks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkLast(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNearSorted(position, radius, comp);
|
||||
return placemarks == null ? null : placemarks.get(placemarks.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkFirst(Position position, double radius, Comparator<Placemark> comp)
|
||||
throws IOException, ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNearSorted(position, radius, comp);
|
||||
return placemarks == null ? null : placemarks.get(0);
|
||||
}
|
||||
|
||||
private List<Placemark> getPlacemarksNearSorted(Position position, double radius, Comparator<Placemark> comp)
|
||||
throws IOException, ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNear(position, radius);
|
||||
if (placemarks != null) {
|
||||
Collections.sort(placemarks, comp);
|
||||
}
|
||||
return placemarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Placemark} for a compatible JSONObject.
|
||||
*
|
||||
* @param json
|
||||
* The object to be converted
|
||||
* @return A {@link Placemark} or <code>null</code>, if the object doesn't contain a name, a postion or the
|
||||
* if the population is 0
|
||||
*/
|
||||
private Placemark JSONToPlacemark(JSONObject json) {
|
||||
String name = (String) json.get("toponymName");
|
||||
String countryCode = (String) json.get("countryCode");
|
||||
|
||||
// Tries are necessary, because some latitude or longitude values delivered by Geonames have no decimal places
|
||||
// and are interpreted as Long
|
||||
// Casting a Long to a Double raises a ClassCastException
|
||||
Double latDeg = null;
|
||||
try {
|
||||
latDeg = (Double) json.get("lat");
|
||||
} catch (ClassCastException e) {
|
||||
latDeg = ((Long) json.get("lat")).doubleValue();
|
||||
}
|
||||
Double lngDeg = null;
|
||||
try {
|
||||
lngDeg = (Double) json.get("lng");
|
||||
} catch (ClassCastException e) {
|
||||
lngDeg = ((Long) json.get("lng")).doubleValue();
|
||||
}
|
||||
SerializablePosition position = new SerializablePositionImpl(latDeg, lngDeg);
|
||||
|
||||
long population = (Long) json.get("population");
|
||||
|
||||
if (name != null && lngDeg != null && latDeg != null) {
|
||||
return new PlacemarkImpl(name, countryCode, position, population);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the <code>position</code>, the <code>radius</code> and the <code>placemarks</code> at the Position
|
||||
* <code>p</code> in the cache.
|
||||
*
|
||||
* @param position
|
||||
* The position in the cache and the point of the search
|
||||
* @param radius
|
||||
* The radius of the search
|
||||
* @param placemarks
|
||||
* The results of the search
|
||||
*/
|
||||
private void cachePlacemarks(Position position, Double radius, List<Placemark> placemarks) {
|
||||
Collections.sort(placemarks, new Placemark.ByDistance(position));
|
||||
if (position != null) {
|
||||
synchronized (cache) {
|
||||
cache.put(position, new Triple<Position, Double, List<Placemark>>(position, radius, placemarks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the data at <code>cachedPoint</code> with the <code>newRadius</code> and <code>newPlacemarks</code>.
|
||||
*
|
||||
* @param cachedPoint
|
||||
* The position of the data which should be replaced. Has to be a Position which was cached before.<br />
|
||||
* The parameter is as best a value from a Triple in the cache, like <code>cachedData.getA()</code>.
|
||||
* @param newRadius
|
||||
* The new radius of the cached search results
|
||||
* @param newPlacemarks
|
||||
* The new search results
|
||||
*/
|
||||
private void updateCachedPlacemarks(Position cachedPoint, Double newRadius, List<Placemark> newPlacemarks) {
|
||||
if (cachedPoint != null) {
|
||||
synchronized (cache) {
|
||||
cache.replace(cachedPoint, new Triple<Position, Double, List<Placemark>>(cachedPoint, newRadius, newPlacemarks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* The position to be checked by the cache
|
||||
* @return The cached placemarks (with meta-data) sorted by distance towards <code>position</code> or
|
||||
* <code>null</code>, if there's nothing cached around <code>position</code> within
|
||||
* {@link ReverseGeocoderImpl#POSITION_CACHE_DISTANCE_LIMIT the distance limit}
|
||||
*/
|
||||
private Triple<Position, Double, List<Placemark>> checkCache(Position position) {
|
||||
synchronized (cache) {
|
||||
return cache.get(position, POSITION_CACHE_DISTANCE_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
return geonames;
|
||||
}
|
||||
|
||||
private JSONArray callNearbyService(Position position, double radius, int maxRows) throws MalformedURLException,
|
||||
IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&radius=" + Double.toString(radius));
|
||||
url.append("&maxRows=" + Integer.toString(maxRows));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
return geonames;
|
||||
}
|
||||
|
||||
final String GEONAMES_USER = "sailtracking";
|
||||
private String getGeonamesUser() {
|
||||
return GEONAMES_USER+new Random().nextInt(10);
|
||||
}
|
||||
|
||||
}
|
||||
package com.sap.sailing.geocoding.impl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.Placemark;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SerializablePosition;
|
||||
import com.sap.sailing.domain.common.impl.PlacemarkImpl;
|
||||
import com.sap.sailing.domain.common.impl.SerializablePositionImpl;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
import com.sap.sailing.domain.common.quadtree.QuadTree;
|
||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
|
||||
public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
private final String NEARBY_PLACE_SERVICE = "http://api.geonames.org/findNearbyPlaceNameJSON?";
|
||||
/**
|
||||
* Maximal distance in degree for the cache.<br />
|
||||
* The first number is the distance in kilometers and the second number is a needed calculation factor and mustn't
|
||||
* be changed! To change the distance just change the first number!
|
||||
*/
|
||||
private final double POSITION_CACHE_DISTANCE_LIMIT = ReverseGeocoder.POSITION_CACHE_DISTANCE_LIMIT_IN_KM * 0.00899928005759539236861051115911;
|
||||
private final int XKM_RADIUS = 5;
|
||||
private final int ROWS_PER_XKM_RADIUS = 15;
|
||||
|
||||
private final int MAX_ROW_NUMBER = 1000;
|
||||
private final int MAX_RADIUS = 300;
|
||||
|
||||
private QuadTree<Triple<Position, Double, List<Placemark>>> cache = new QuadTree<Triple<Position,Double,List<Placemark>>>();;
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkNearest(Position position) throws IOException, ParseException {
|
||||
Placemark p = null;
|
||||
Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getC() != null && !cachedPlacemarks.getC().isEmpty()) {
|
||||
p = cachedPlacemarks.getC().get(0);
|
||||
} else {
|
||||
JSONArray geonames = callNearestService(position);
|
||||
if (geonames != null && !geonames.isEmpty()) {
|
||||
p = JSONToPlacemark((JSONObject) geonames.get(0));
|
||||
|
||||
if (p != null) {
|
||||
List<Placemark> placemarks = new ArrayList<Placemark>();
|
||||
placemarks.add(p);
|
||||
cachePlacemarks(position, 0.0, placemarks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException {
|
||||
List<Placemark> placemarks = null;
|
||||
Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
|
||||
//Calculating the search radius and the maximum number of returning Placemarks
|
||||
double limitedRadius = Math.min(radius, MAX_RADIUS);
|
||||
int radiusInt = (int) limitedRadius;
|
||||
int xKmRadius = radiusInt / XKM_RADIUS;
|
||||
int maxRows = (int) (ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius));
|
||||
maxRows = Math.min(maxRows, MAX_ROW_NUMBER);
|
||||
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getB() >= limitedRadius) {
|
||||
if (cachedPlacemarks.getC().size() > maxRows) {
|
||||
placemarks = cachedPlacemarks.getC().subList(0, maxRows);
|
||||
} else {
|
||||
placemarks = new ArrayList<Placemark>(cachedPlacemarks.getC());
|
||||
}
|
||||
} else {
|
||||
//Recalculating the radius and resetting the search position to keep the cache correct
|
||||
Position searchPosition = null;
|
||||
if (cachedPlacemarks != null) {
|
||||
searchPosition = cachedPlacemarks.getA();
|
||||
double distance = position.getDistance(searchPosition).getKilometers();
|
||||
limitedRadius = Math.min(MAX_RADIUS, limitedRadius + distance);
|
||||
} else {
|
||||
searchPosition = position;
|
||||
}
|
||||
|
||||
JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows);
|
||||
if (geonames != null) {
|
||||
Iterator<Object> iterator = geonames.iterator();
|
||||
placemarks = iterator.hasNext() ? new ArrayList<Placemark>() : null;
|
||||
while (iterator.hasNext()) {
|
||||
JSONObject object = (JSONObject) iterator.next();
|
||||
Placemark place = JSONToPlacemark(object);
|
||||
if (place != null) {
|
||||
placemarks.add(JSONToPlacemark(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no cached placemarks for the requested Position just cache them, otherwise update the cache
|
||||
if (cachedPlacemarks == null && placemarks != null) {
|
||||
cachePlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
} else if (placemarks != null) {
|
||||
updateCachedPlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
}
|
||||
}
|
||||
|
||||
return placemarks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkLast(Position position, double radius, Comparator<Placemark> comp) throws IOException,
|
||||
ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNearSorted(position, radius, comp);
|
||||
return placemarks == null ? null : placemarks.get(placemarks.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkFirst(Position position, double radius, Comparator<Placemark> comp)
|
||||
throws IOException, ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNearSorted(position, radius, comp);
|
||||
return placemarks == null ? null : placemarks.get(0);
|
||||
}
|
||||
|
||||
private List<Placemark> getPlacemarksNearSorted(Position position, double radius, Comparator<Placemark> comp)
|
||||
throws IOException, ParseException {
|
||||
List<Placemark> placemarks = getPlacemarksNear(position, radius);
|
||||
if (placemarks != null) {
|
||||
Collections.sort(placemarks, comp);
|
||||
}
|
||||
return placemarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Placemark} for a compatible JSONObject.
|
||||
*
|
||||
* @param json
|
||||
* The object to be converted
|
||||
* @return A {@link Placemark} or <code>null</code>, if the object doesn't contain a name, a postion or the
|
||||
* if the population is 0
|
||||
*/
|
||||
private Placemark JSONToPlacemark(JSONObject json) {
|
||||
String name = (String) json.get("toponymName");
|
||||
String countryCode = (String) json.get("countryCode");
|
||||
|
||||
// Tries are necessary, because some latitude or longitude values delivered by Geonames have no decimal places
|
||||
// and are interpreted as Long
|
||||
// Casting a Long to a Double raises a ClassCastException
|
||||
Double latDeg = null;
|
||||
try {
|
||||
latDeg = (Double) json.get("lat");
|
||||
} catch (ClassCastException e) {
|
||||
latDeg = ((Long) json.get("lat")).doubleValue();
|
||||
}
|
||||
Double lngDeg = null;
|
||||
try {
|
||||
lngDeg = (Double) json.get("lng");
|
||||
} catch (ClassCastException e) {
|
||||
lngDeg = ((Long) json.get("lng")).doubleValue();
|
||||
}
|
||||
SerializablePosition position = new SerializablePositionImpl(latDeg, lngDeg);
|
||||
|
||||
long population = (Long) json.get("population");
|
||||
|
||||
if (name != null && lngDeg != null && latDeg != null) {
|
||||
return new PlacemarkImpl(name, countryCode, position, population);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the <code>position</code>, the <code>radius</code> and the <code>placemarks</code> at the Position
|
||||
* <code>p</code> in the cache.
|
||||
*
|
||||
* @param position
|
||||
* The position in the cache and the point of the search
|
||||
* @param radius
|
||||
* The radius of the search
|
||||
* @param placemarks
|
||||
* The results of the search
|
||||
*/
|
||||
private void cachePlacemarks(Position position, Double radius, List<Placemark> placemarks) {
|
||||
Collections.sort(placemarks, new Placemark.ByDistance(position));
|
||||
if (position != null) {
|
||||
synchronized (cache) {
|
||||
cache.put(position, new Triple<Position, Double, List<Placemark>>(position, radius, placemarks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the data at <code>cachedPoint</code> with the <code>newRadius</code> and <code>newPlacemarks</code>.
|
||||
*
|
||||
* @param cachedPoint
|
||||
* The position of the data which should be replaced. Has to be a Position which was cached before.<br />
|
||||
* The parameter is as best a value from a Triple in the cache, like <code>cachedData.getA()</code>.
|
||||
* @param newRadius
|
||||
* The new radius of the cached search results
|
||||
* @param newPlacemarks
|
||||
* The new search results
|
||||
*/
|
||||
private void updateCachedPlacemarks(Position cachedPoint, Double newRadius, List<Placemark> newPlacemarks) {
|
||||
if (cachedPoint != null) {
|
||||
synchronized (cache) {
|
||||
cache.replace(cachedPoint, new Triple<Position, Double, List<Placemark>>(cachedPoint, newRadius, newPlacemarks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* The position to be checked by the cache
|
||||
* @return The cached placemarks (with meta-data) sorted by distance towards <code>position</code> or
|
||||
* <code>null</code>, if there's nothing cached around <code>position</code> within
|
||||
* {@link ReverseGeocoderImpl#POSITION_CACHE_DISTANCE_LIMIT the distance limit}
|
||||
*/
|
||||
private Triple<Position, Double, List<Placemark>> checkCache(Position position) {
|
||||
synchronized (cache) {
|
||||
return cache.get(position, POSITION_CACHE_DISTANCE_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
return geonames;
|
||||
}
|
||||
|
||||
private JSONArray callNearbyService(Position position, double radius, int maxRows) throws MalformedURLException,
|
||||
IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("&lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
url.append("&radius=" + Double.toString(radius));
|
||||
url.append("&maxRows=" + Integer.toString(maxRows));
|
||||
url.append("&username=" + getGeonamesUser());
|
||||
|
||||
URL request = new URL(url.toString());
|
||||
URLConnection connection = request.openConnection();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
return geonames;
|
||||
}
|
||||
|
||||
final String GEONAMES_USER = "sailtracking";
|
||||
private String getGeonamesUser() {
|
||||
return GEONAMES_USER+new Random().nextInt(10);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user