From 404044342a9fc9891366fa4378b4b1330cbca6c3 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Wed, 8 Nov 2017 15:43:13 +0500 Subject: - Implement synchronous geocoding - Implement retry geocoding for trips/stops reports - Implement API for revers geocoding --- src/org/traccar/BasePipelineFactory.java | 2 +- src/org/traccar/Context.java | 71 ++++++++++++----------- src/org/traccar/GeocoderHandler.java | 11 +--- src/org/traccar/api/resource/ServerResource.java | 9 ++- src/org/traccar/geocoder/BingMapsGeocoder.java | 5 +- src/org/traccar/geocoder/FactualGeocoder.java | 5 +- src/org/traccar/geocoder/GeocodeFarmGeocoder.java | 6 +- src/org/traccar/geocoder/Geocoder.java | 6 +- src/org/traccar/geocoder/GisgraphyGeocoder.java | 10 ++-- src/org/traccar/geocoder/GoogleGeocoder.java | 6 +- src/org/traccar/geocoder/JsonGeocoder.java | 48 +++++++++++++-- src/org/traccar/geocoder/MapQuestGeocoder.java | 5 +- src/org/traccar/geocoder/NominatimGeocoder.java | 6 +- src/org/traccar/geocoder/OpenCageGeocoder.java | 6 +- src/org/traccar/reports/ReportUtils.java | 19 +++++- 15 files changed, 136 insertions(+), 79 deletions(-) (limited to 'src/org') diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java index 771ab8acb..27b9678a3 100644 --- a/src/org/traccar/BasePipelineFactory.java +++ b/src/org/traccar/BasePipelineFactory.java @@ -141,7 +141,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory { filterHandler = new FilterHandler(); } - if (Context.getGeocoder() != null) { + if (Context.getConfig().getBoolean("geocoder.enable")) { geocoderHandler = new GeocoderHandler( Context.getGeocoder(), Context.getConfig().getBoolean("geocoder.processInvalidPositions")); diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 3b24c6460..0671f1d29 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -43,6 +43,7 @@ import org.traccar.database.StatisticsManager; import org.traccar.database.UsersManager; import org.traccar.events.MotionEventHandler; import org.traccar.events.OverspeedEventHandler; +import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.BingMapsGeocoder; import org.traccar.geocoder.FactualGeocoder; import org.traccar.geocoder.GeocodeFarmGeocoder; @@ -263,6 +264,41 @@ public final class Context { config.getDouble("event.motion.speedThreshold", 0.01)); } + public static Geocoder initGeocoder() { + String type = config.getString("geocoder.type", "google"); + String url = config.getString("geocoder.url"); + String key = config.getString("geocoder.key"); + String language = config.getString("geocoder.language"); + + String formatString = config.getString("geocoder.format"); + AddressFormat addressFormat; + if (formatString != null) { + addressFormat = new AddressFormat(formatString); + } else { + addressFormat = new AddressFormat(); + } + + int cacheSize = config.getInteger("geocoder.cacheSize"); + switch (type) { + case "nominatim": + return new NominatimGeocoder(url, key, language, cacheSize, addressFormat); + case "gisgraphy": + return new GisgraphyGeocoder(url, cacheSize, addressFormat); + case "mapquest": + return new MapQuestGeocoder(url, key, cacheSize, addressFormat); + case "opencage": + return new OpenCageGeocoder(url, key, cacheSize, addressFormat); + case "bingmaps": + return new BingMapsGeocoder(url, key, cacheSize, addressFormat); + case "factual": + return new FactualGeocoder(url, key, cacheSize, addressFormat); + case "geocodefarm": + return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat); + default: + return new GoogleGeocoder(key, language, cacheSize, addressFormat); + } + } + public static void init(String[] arguments) throws Exception { config = new Config(); @@ -297,39 +333,8 @@ public final class Context { identityManager = deviceManager; - if (config.getBoolean("geocoder.enable")) { - String type = config.getString("geocoder.type", "google"); - String url = config.getString("geocoder.url"); - String key = config.getString("geocoder.key"); - String language = config.getString("geocoder.language"); - - int cacheSize = config.getInteger("geocoder.cacheSize"); - switch (type) { - case "nominatim": - geocoder = new NominatimGeocoder(url, key, language, cacheSize); - break; - case "gisgraphy": - geocoder = new GisgraphyGeocoder(url, cacheSize); - break; - case "mapquest": - geocoder = new MapQuestGeocoder(url, key, cacheSize); - break; - case "opencage": - geocoder = new OpenCageGeocoder(url, key, cacheSize); - break; - case "bingmaps": - geocoder = new BingMapsGeocoder(url, key, cacheSize); - break; - case "factual": - geocoder = new FactualGeocoder(url, key, cacheSize); - break; - case "geocodefarm": - geocoder = new GeocodeFarmGeocoder(key, language, cacheSize); - break; - default: - geocoder = new GoogleGeocoder(key, language, cacheSize); - break; - } + if (config.getBoolean("geocoder.enable") || config.getBoolean("report.retryGeocoding")) { + geocoder = initGeocoder(); } if (config.getBoolean("geolocation.enable")) { diff --git a/src/org/traccar/GeocoderHandler.java b/src/org/traccar/GeocoderHandler.java index a211d1a23..6b55e8162 100644 --- a/src/org/traccar/GeocoderHandler.java +++ b/src/org/traccar/GeocoderHandler.java @@ -20,7 +20,6 @@ import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelUpstreamHandler; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.MessageEvent; -import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.Geocoder; import org.traccar.helper.Log; import org.traccar.model.Position; @@ -29,20 +28,12 @@ public class GeocoderHandler implements ChannelUpstreamHandler { private final Geocoder geocoder; private final boolean processInvalidPositions; - private final AddressFormat addressFormat; private final int geocoderReuseDistance; public GeocoderHandler(Geocoder geocoder, boolean processInvalidPositions) { this.geocoder = geocoder; this.processInvalidPositions = processInvalidPositions; - String formatString = Context.getConfig().getString("geocoder.format"); - if (formatString != null) { - addressFormat = new AddressFormat(formatString); - } else { - addressFormat = new AddressFormat(); - } - geocoderReuseDistance = Context.getConfig().getInteger("geocoder.reuseDistance", 0); } @@ -70,7 +61,7 @@ public class GeocoderHandler implements ChannelUpstreamHandler { Context.getStatisticsManager().registerGeocoderRequest(); - geocoder.getAddress(addressFormat, position.getLatitude(), position.getLongitude(), + geocoder.getAddress(position.getLatitude(), position.getLongitude(), new Geocoder.ReverseGeocoderCallback() { @Override public void onSuccess(String address) { diff --git a/src/org/traccar/api/resource/ServerResource.java b/src/org/traccar/api/resource/ServerResource.java index 034a5c492..b803c3380 100644 --- a/src/org/traccar/api/resource/ServerResource.java +++ b/src/org/traccar/api/resource/ServerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.sql.SQLException; @@ -47,4 +48,10 @@ public class ServerResource extends BaseResource { return Response.ok(entity).build(); } + @Path("geocoding") + @GET + public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) { + return Context.getGeocoder().getAddress(latitude, longitude); + } + } diff --git a/src/org/traccar/geocoder/BingMapsGeocoder.java b/src/org/traccar/geocoder/BingMapsGeocoder.java index a9b36219a..fbfb5394a 100644 --- a/src/org/traccar/geocoder/BingMapsGeocoder.java +++ b/src/org/traccar/geocoder/BingMapsGeocoder.java @@ -1,5 +1,6 @@ /* * Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com) + * Copyright 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +21,8 @@ import javax.json.JsonObject; public class BingMapsGeocoder extends JsonGeocoder { - public BingMapsGeocoder(String url, String key, int cacheSize) { - super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize); + public BingMapsGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { + super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/FactualGeocoder.java b/src/org/traccar/geocoder/FactualGeocoder.java index 0c76e4625..c7a68c293 100644 --- a/src/org/traccar/geocoder/FactualGeocoder.java +++ b/src/org/traccar/geocoder/FactualGeocoder.java @@ -1,5 +1,6 @@ /* * Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com) + * Copyright 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +20,8 @@ import javax.json.JsonObject; public class FactualGeocoder extends JsonGeocoder { - public FactualGeocoder(String url, String key, int cacheSize) { - super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize); + public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { + super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java index 73ff85aa4..4fb956af9 100644 --- a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java +++ b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,8 @@ public class GeocodeFarmGeocoder extends JsonGeocoder { } return url; } - public GeocodeFarmGeocoder(String key, String language, int cacheSize) { - super(formatUrl(key, language), cacheSize); + public GeocodeFarmGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) { + super(formatUrl(key, language), cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/Geocoder.java b/src/org/traccar/geocoder/Geocoder.java index 3ce3fb67f..c3fa394c7 100644 --- a/src/org/traccar/geocoder/Geocoder.java +++ b/src/org/traccar/geocoder/Geocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2013 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ public interface Geocoder { } - void getAddress(AddressFormat format, double latitude, double longitude, ReverseGeocoderCallback callback); + void getAddress(double latitude, double longitude, ReverseGeocoderCallback callback); + + String getAddress(double latitude, double longitude); } diff --git a/src/org/traccar/geocoder/GisgraphyGeocoder.java b/src/org/traccar/geocoder/GisgraphyGeocoder.java index 1432166e9..a0c831966 100644 --- a/src/org/traccar/geocoder/GisgraphyGeocoder.java +++ b/src/org/traccar/geocoder/GisgraphyGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,12 @@ import javax.json.JsonObject; public class GisgraphyGeocoder extends JsonGeocoder { - public GisgraphyGeocoder() { - this("http://services.gisgraphy.com/reversegeocoding/search", 0); + public GisgraphyGeocoder(AddressFormat addressFormat) { + this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat); } - public GisgraphyGeocoder(String url, int cacheSize) { - super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize); + public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) { + super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/GoogleGeocoder.java b/src/org/traccar/geocoder/GoogleGeocoder.java index b38870c8f..235ea9ea9 100644 --- a/src/org/traccar/geocoder/GoogleGeocoder.java +++ b/src/org/traccar/geocoder/GoogleGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,8 @@ public class GoogleGeocoder extends JsonGeocoder { return url; } - public GoogleGeocoder(String key, String language, int cacheSize) { - super(formatUrl(key, language), cacheSize); + public GoogleGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) { + super(formatUrl(key, language), cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/JsonGeocoder.java b/src/org/traccar/geocoder/JsonGeocoder.java index 6d1380729..2ae1fe5aa 100644 --- a/src/org/traccar/geocoder/JsonGeocoder.java +++ b/src/org/traccar/geocoder/JsonGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,23 +18,30 @@ package org.traccar.geocoder; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.Response; import org.traccar.Context; +import org.traccar.helper.Log; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; + +import java.io.IOException; import java.util.AbstractMap; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.ExecutionException; public abstract class JsonGeocoder implements Geocoder { private final String url; + private final AddressFormat addressFormat; private Map, String> cache; - public JsonGeocoder(String url, final int cacheSize) { + public JsonGeocoder(String url, final int cacheSize, AddressFormat addressFormat) { this.url = url; + this.addressFormat = addressFormat; if (cacheSize > 0) { this.cache = Collections.synchronizedMap(new LinkedHashMap, String>() { @Override @@ -47,8 +54,7 @@ public abstract class JsonGeocoder implements Geocoder { @Override public void getAddress( - final AddressFormat format, final double latitude, - final double longitude, final ReverseGeocoderCallback callback) { + final double latitude, final double longitude, final ReverseGeocoderCallback callback) { if (cache != null) { String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude)); @@ -58,14 +64,14 @@ public abstract class JsonGeocoder implements Geocoder { } } - Context.getAsyncHttpClient().prepareGet(String.format(url, latitude, longitude)) + Context.getAsyncHttpClient().prepareGet(String.format(Locale.US, url, latitude, longitude)) .execute(new AsyncCompletionHandler() { @Override public Object onCompleted(Response response) throws Exception { try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { Address address = parseAddress(reader.readObject()); if (address != null) { - String formattedAddress = format.format(address); + String formattedAddress = addressFormat.format(address); if (cache != null) { cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress); } @@ -84,6 +90,36 @@ public abstract class JsonGeocoder implements Geocoder { }); } + @Override + public String getAddress(final double latitude, final double longitude) { + if (cache != null) { + String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude)); + if (cachedAddress != null) { + return cachedAddress; + } + } + + try { + Response response = Context.getAsyncHttpClient() + .prepareGet(String.format(Locale.US, url, latitude, longitude)).execute().get(); + try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { + Address address = parseAddress(reader.readObject()); + if (address != null) { + String formattedAddress = addressFormat.format(address); + if (cache != null) { + cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress); + } + return formattedAddress; + } else { + Log.warning("Empty address"); + } + } + } catch (InterruptedException | ExecutionException | IOException error) { + Log.warning("Geocoding failed", error); + } + return null; + } + public abstract Address parseAddress(JsonObject json); } diff --git a/src/org/traccar/geocoder/MapQuestGeocoder.java b/src/org/traccar/geocoder/MapQuestGeocoder.java index 7d7217e91..4029e3f07 100644 --- a/src/org/traccar/geocoder/MapQuestGeocoder.java +++ b/src/org/traccar/geocoder/MapQuestGeocoder.java @@ -1,5 +1,6 @@ /* * Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com) + * Copyright 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +21,8 @@ import javax.json.JsonObject; public class MapQuestGeocoder extends JsonGeocoder { - public MapQuestGeocoder(String url, String key, int cacheSize) { - super(url + "?key=" + key + "&location=%f,%f", cacheSize); + public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { + super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/NominatimGeocoder.java b/src/org/traccar/geocoder/NominatimGeocoder.java index 051727e93..75d2e9f99 100644 --- a/src/org/traccar/geocoder/NominatimGeocoder.java +++ b/src/org/traccar/geocoder/NominatimGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,8 @@ public class NominatimGeocoder extends JsonGeocoder { return url; } - public NominatimGeocoder(String url, String key, String language, int cacheSize) { - super(formatUrl(url, key, language), cacheSize); + public NominatimGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) { + super(formatUrl(url, key, language), cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/geocoder/OpenCageGeocoder.java b/src/org/traccar/geocoder/OpenCageGeocoder.java index 9fa56a4a3..894397ee3 100644 --- a/src/org/traccar/geocoder/OpenCageGeocoder.java +++ b/src/org/traccar/geocoder/OpenCageGeocoder.java @@ -1,6 +1,6 @@ /* * Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com) - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import javax.json.JsonObject; public class OpenCageGeocoder extends JsonGeocoder { - public OpenCageGeocoder(String url, String key, int cacheSize) { - super(url + "/json?q=%f,%f&key=" + key, cacheSize); + public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) { + super(url + "/json?q=%f,%f&key=" + key, cacheSize, addressFormat); } @Override diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java index f6f386e99..f0d6d40c2 100644 --- a/src/org/traccar/reports/ReportUtils.java +++ b/src/org/traccar/reports/ReportUtils.java @@ -190,13 +190,21 @@ public final class ReportUtils { trip.setStartLat(startTrip.getLatitude()); trip.setStartLon(startTrip.getLongitude()); trip.setStartTime(startTrip.getFixTime()); - trip.setStartAddress(startTrip.getAddress()); + String startAddress = startTrip.getAddress(); + if (startAddress == null && Context.getConfig().getBoolean("report.retryGeocoding")) { + startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude()); + } + trip.setStartAddress(startAddress); trip.setEndPositionId(endTrip.getId()); trip.setEndLat(endTrip.getLatitude()); trip.setEndLon(endTrip.getLongitude()); trip.setEndTime(endTrip.getFixTime()); - trip.setEndAddress(endTrip.getAddress()); + String endAddress = endTrip.getAddress(); + if (endAddress == null && Context.getConfig().getBoolean("report.retryGeocoding")) { + endAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude()); + } + trip.setEndAddress(endAddress); trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer)); trip.setDuration(tripDuration); @@ -224,7 +232,12 @@ public final class ReportUtils { stop.setLatitude(startStop.getLatitude()); stop.setLongitude(startStop.getLongitude()); stop.setStartTime(startStop.getFixTime()); - stop.setAddress(startStop.getAddress()); + String address = startStop.getAddress(); + if (address == null && Context.getConfig().getBoolean("report.retryGeocoding")) { + address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude()); + } + stop.setAddress(address); + stop.setEndTime(endStop.getFixTime()); long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime(); -- cgit v1.2.3