From d610b1ab5135503d1d39aa291190a860ddb952a9 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 24 Feb 2019 15:12:26 -0800 Subject: Refactor geocoder handler --- src/org/traccar/BasePipelineFactory.java | 10 +-- src/org/traccar/Context.java | 65 +------------------ src/org/traccar/GeocoderHandler.java | 82 ------------------------ src/org/traccar/MainEventHandler.java | 1 + src/org/traccar/MainModule.java | 57 +++++++++++++++++ src/org/traccar/config/Keys.java | 68 ++++++++++++++++++++ src/org/traccar/handler/GeocoderHandler.java | 94 ++++++++++++++++++++++++++++ 7 files changed, 223 insertions(+), 154 deletions(-) delete mode 100644 src/org/traccar/GeocoderHandler.java create mode 100644 src/org/traccar/handler/GeocoderHandler.java diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java index 082d666ac..8e6a62391 100644 --- a/src/org/traccar/BasePipelineFactory.java +++ b/src/org/traccar/BasePipelineFactory.java @@ -38,6 +38,7 @@ import org.traccar.handler.ComputedAttributesHandler; import org.traccar.handler.CopyAttributesHandler; import org.traccar.handler.DistanceHandler; import org.traccar.handler.FilterHandler; +import org.traccar.handler.GeocoderHandler; import org.traccar.handler.GeolocationHandler; import org.traccar.handler.HemisphereHandler; import org.traccar.handler.NetworkMessageHandler; @@ -56,7 +57,6 @@ public abstract class BasePipelineFactory extends ChannelInitializer { private EngineHoursHandler engineHoursHandler; private MotionHandler motionHandler; - private GeocoderHandler geocoderHandler; private CopyAttributesHandler copyAttributesHandler; private ComputedAttributesHandler computedAttributesHandler; @@ -78,12 +78,6 @@ public abstract class BasePipelineFactory extends ChannelInitializer { timeout = Context.getConfig().getInteger(Keys.SERVER_TIMEOUT); } - if (Context.getGeocoder() != null && !Context.getConfig().getBoolean("geocoder.ignorePositions")) { - geocoderHandler = new GeocoderHandler( - Context.getGeocoder(), - Context.getConfig().getBoolean("geocoder.processInvalidPositions")); - } - motionHandler = new MotionHandler(Context.getTripsConfig().getSpeedThreshold()); if (Context.getConfig().getBoolean("processing.engineHours.enable")) { @@ -170,7 +164,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer { addHandlers( pipeline, Main.getInjector().getInstance(FilterHandler.class), - geocoderHandler, + Main.getInjector().getInstance(GeocoderHandler.class), motionHandler, engineHoursHandler, copyAttributesHandler, diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index e507406e8..2f8432645 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -43,20 +43,7 @@ import org.traccar.database.PermissionsManager; import org.traccar.database.UsersManager; import org.traccar.events.MotionEventHandler; import org.traccar.events.OverspeedEventHandler; -import org.traccar.geocoder.AddressFormat; -import org.traccar.geocoder.BanGeocoder; -import org.traccar.geocoder.BingMapsGeocoder; -import org.traccar.geocoder.FactualGeocoder; -import org.traccar.geocoder.GeocodeFarmGeocoder; -import org.traccar.geocoder.GeocodeXyzGeocoder; import org.traccar.geocoder.Geocoder; -import org.traccar.geocoder.GisgraphyGeocoder; -import org.traccar.geocoder.GoogleGeocoder; -import org.traccar.geocoder.HereGeocoder; -import org.traccar.geocoder.MapQuestGeocoder; -import org.traccar.geocoder.MapmyIndiaGeocoder; -import org.traccar.geocoder.NominatimGeocoder; -import org.traccar.geocoder.OpenCageGeocoder; import org.traccar.helper.Log; import org.traccar.helper.SanitizerModule; import org.traccar.model.Attribute; @@ -164,10 +151,8 @@ public final class Context { return permissionsManager; } - private static Geocoder geocoder; - public static Geocoder getGeocoder() { - return geocoder; + return Main.getInjector() != null ? Main.getInjector().getInstance(Geocoder.class) : null; } private static WebServer webServer; @@ -292,50 +277,6 @@ public final class Context { } - public static Geocoder initGeocoder() { - String type = config.getString("geocoder.type", "google"); - String url = config.getString("geocoder.url"); - String id = config.getString("geocoder.id"); - 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); - case "geocodexyz": - return new GeocodeXyzGeocoder(key, cacheSize, addressFormat); - case "ban": - return new BanGeocoder(cacheSize, addressFormat); - case "here": - return new HereGeocoder(id, key, language, cacheSize, addressFormat); - case "mapmyindia": - return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat); - default: - return new GoogleGeocoder(key, language, cacheSize, addressFormat); - } - } - public static void init(String configFile) throws Exception { try { @@ -381,10 +322,6 @@ public final class Context { identityManager = deviceManager; - if (config.getBoolean("geocoder.enable")) { - geocoder = initGeocoder(); - } - if (config.getBoolean("web.enable")) { webServer = new WebServer(config); } diff --git a/src/org/traccar/GeocoderHandler.java b/src/org/traccar/GeocoderHandler.java deleted file mode 100644 index 4154e287c..000000000 --- a/src/org/traccar/GeocoderHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 - 2018 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. - * 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.traccar; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.database.StatisticsManager; -import org.traccar.geocoder.Geocoder; -import org.traccar.model.Position; - -@ChannelHandler.Sharable -public class GeocoderHandler extends ChannelInboundHandlerAdapter { - - private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class); - - private final Geocoder geocoder; - private final boolean processInvalidPositions; - private final int geocoderReuseDistance; - - public GeocoderHandler(Geocoder geocoder, boolean processInvalidPositions) { - this.geocoder = geocoder; - this.processInvalidPositions = processInvalidPositions; - - geocoderReuseDistance = Context.getConfig().getInteger("geocoder.reuseDistance", 0); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object message) throws Exception { - if (message instanceof Position) { - final Position position = (Position) message; - if (processInvalidPositions || position.getValid()) { - if (geocoderReuseDistance != 0) { - Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId()); - if (lastPosition != null && lastPosition.getAddress() != null - && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) { - position.setAddress(lastPosition.getAddress()); - ctx.fireChannelRead(position); - return; - } - } - - Main.getInjector().getInstance(StatisticsManager.class).registerGeocoderRequest(); - - geocoder.getAddress(position.getLatitude(), position.getLongitude(), - new Geocoder.ReverseGeocoderCallback() { - @Override - public void onSuccess(String address) { - position.setAddress(address); - ctx.fireChannelRead(position); - } - - @Override - public void onFailure(Throwable e) { - LOGGER.warn("Geocoding failed", e); - ctx.fireChannelRead(position); - } - }); - } else { - ctx.fireChannelRead(position); - } - } else { - ctx.fireChannelRead(message); - } - } - -} diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java index 1137bfc24..80c05e3c7 100644 --- a/src/org/traccar/MainEventHandler.java +++ b/src/org/traccar/MainEventHandler.java @@ -24,6 +24,7 @@ import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.database.StatisticsManager; +import org.traccar.handler.GeocoderHandler; import org.traccar.helper.DateUtil; import org.traccar.model.Position; diff --git a/src/org/traccar/MainModule.java b/src/org/traccar/MainModule.java index 0e5c7d858..c6ca65088 100644 --- a/src/org/traccar/MainModule.java +++ b/src/org/traccar/MainModule.java @@ -24,6 +24,20 @@ import org.traccar.config.Keys; import org.traccar.database.DataManager; import org.traccar.database.IdentityManager; import org.traccar.database.StatisticsManager; +import org.traccar.geocoder.AddressFormat; +import org.traccar.geocoder.BanGeocoder; +import org.traccar.geocoder.BingMapsGeocoder; +import org.traccar.geocoder.FactualGeocoder; +import org.traccar.geocoder.GeocodeFarmGeocoder; +import org.traccar.geocoder.GeocodeXyzGeocoder; +import org.traccar.geocoder.Geocoder; +import org.traccar.geocoder.GisgraphyGeocoder; +import org.traccar.geocoder.GoogleGeocoder; +import org.traccar.geocoder.HereGeocoder; +import org.traccar.geocoder.MapQuestGeocoder; +import org.traccar.geocoder.MapmyIndiaGeocoder; +import org.traccar.geocoder.NominatimGeocoder; +import org.traccar.geocoder.OpenCageGeocoder; import org.traccar.geolocation.GeolocationProvider; import org.traccar.geolocation.GoogleGeolocationProvider; import org.traccar.geolocation.MozillaGeolocationProvider; @@ -71,6 +85,49 @@ public class MainModule extends AbstractModule { return new StatisticsManager(config, dataManager, client); } + @Singleton + @Provides + public static Geocoder provideGeocoder(Config config) { + if (config.getBoolean(Keys.GEOCODER_ENABLE)) { + String type = config.getString(Keys.GEOCODER_TYPE, "google"); + String url = config.getString(Keys.GEOCODER_URL); + String id = config.getString(Keys.GEOCODER_ID); + String key = config.getString(Keys.GEOCODER_KEY); + String language = config.getString(Keys.GEOCODER_LANGUAGE); + String formatString = config.getString(Keys.GEOCODER_FORMAT); + AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat(); + + int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE); + 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); + case "geocodexyz": + return new GeocodeXyzGeocoder(key, cacheSize, addressFormat); + case "ban": + return new BanGeocoder(cacheSize, addressFormat); + case "here": + return new HereGeocoder(id, key, language, cacheSize, addressFormat); + case "mapmyindia": + return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat); + default: + return new GoogleGeocoder(key, language, cacheSize, addressFormat); + } + } + return null; + } + @Singleton @Provides public static GeolocationProvider provideGeolocationProvider(Config config) { diff --git a/src/org/traccar/config/Keys.java b/src/org/traccar/config/Keys.java index fddc70bba..7449bfdf5 100644 --- a/src/org/traccar/config/Keys.java +++ b/src/org/traccar/config/Keys.java @@ -179,6 +179,74 @@ public final class Keys { public static final ConfigKey PROCESSING_REMOTE_ADDRESS_ENABLE = new ConfigKey( "processing.remoteAddress.enable", Boolean.class); + /** + * Boolean flag to enable or disable reverse geocoder. + */ + public static final ConfigKey GEOCODER_ENABLE = new ConfigKey( + "geocoder.enable", Boolean.class); + + /** + * Reverse geocoder type. Check reverse geocoding documentation for more info. By default (if the value is not + * specified) server uses Google API. + */ + public static final ConfigKey GEOCODER_TYPE = new ConfigKey( + "geocoder.type", String.class); + + /** + * Geocoder server URL. Applicable only to Nominatim and Gisgraphy providers. + */ + public static final ConfigKey GEOCODER_URL = new ConfigKey( + "geocoder.url", String.class); + + /** + * App id for use with Here provider. + */ + public static final ConfigKey GEOCODER_ID = new ConfigKey( + "geocoder.id", String.class); + + /** + * Provider API key. Most providers require API keys. + */ + public static final ConfigKey GEOCODER_KEY = new ConfigKey( + "geocoder.key", String.class); + + /** + * Language parameter for providers that support localization (e.g. Google and Nominatim). + */ + public static final ConfigKey GEOCODER_LANGUAGE = new ConfigKey( + "geocoder.language", String.class); + + /** + * Address format string. Default value is %h %r, %t, %s, %c. See AddressFormat for more info. + */ + public static final ConfigKey GEOCODER_FORMAT = new ConfigKey( + "geocoder.format", String.class); + + /** + * Cache size for geocoding results. + */ + public static final ConfigKey GEOCODER_CACHE_SIZE = new ConfigKey( + "geocoder.cacheSize", Integer.class); + + /** + * Disable automatic reverse geocoding requests for all positions. + */ + public static final ConfigKey GEOCODER_IGNORE_POSITIONS = new ConfigKey( + "geocoder.ignorePositions", Boolean.class); + + /** + * Boolean flag to apply reverse geocoding to invalid positions. + */ + public static final ConfigKey GEOCODER_PROCESS_INVALID_POSITIONS = new ConfigKey( + "geocoder.processInvalidPositions", Boolean.class); + + /** + * Optional parameter to specify minimum distance for new reverse geocoding request. If distance is less than + * specified value (in meters), then Traccar will reuse last known address. + */ + public static final ConfigKey GEOCODER_REUSE_DISTANCE = new ConfigKey( + "geocoder.reuseDistance", Integer.class); + /** * Boolean flag to enable LBS location resolution. Some devices send cell towers information and WiFi point when GPS * location is not available. Traccar can determine coordinates based on that information using third party diff --git a/src/org/traccar/handler/GeocoderHandler.java b/src/org/traccar/handler/GeocoderHandler.java new file mode 100644 index 000000000..b96f01b3a --- /dev/null +++ b/src/org/traccar/handler/GeocoderHandler.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012 - 2019 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. + * 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.traccar.handler; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Context; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.database.IdentityManager; +import org.traccar.database.StatisticsManager; +import org.traccar.geocoder.Geocoder; +import org.traccar.model.Position; + +@ChannelHandler.Sharable +public class GeocoderHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class); + + private final Geocoder geocoder; + private final IdentityManager identityManager; + private final StatisticsManager statisticsManager; + private final boolean ignorePositions; + private final boolean processInvalidPositions; + private final int geocoderReuseDistance; + + public GeocoderHandler( + Config config, Geocoder geocoder, IdentityManager identityManager, StatisticsManager statisticsManager) { + this.geocoder = geocoder; + this.identityManager = identityManager; + this.statisticsManager = statisticsManager; + ignorePositions = Context.getConfig().getBoolean(Keys.GEOCODER_IGNORE_POSITIONS); + processInvalidPositions = config.getBoolean(Keys.GEOCODER_PROCESS_INVALID_POSITIONS); + geocoderReuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0); + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, Object message) { + if (message instanceof Position && !ignorePositions) { + final Position position = (Position) message; + if (processInvalidPositions || position.getValid()) { + if (geocoderReuseDistance != 0) { + Position lastPosition = identityManager.getLastPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.getAddress() != null + && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) { + position.setAddress(lastPosition.getAddress()); + ctx.fireChannelRead(position); + return; + } + } + + if (statisticsManager != null) { + statisticsManager.registerGeocoderRequest(); + } + + geocoder.getAddress(position.getLatitude(), position.getLongitude(), + new Geocoder.ReverseGeocoderCallback() { + @Override + public void onSuccess(String address) { + position.setAddress(address); + ctx.fireChannelRead(position); + } + + @Override + public void onFailure(Throwable e) { + LOGGER.warn("Geocoding failed", e); + ctx.fireChannelRead(position); + } + }); + } else { + ctx.fireChannelRead(position); + } + } else { + ctx.fireChannelRead(message); + } + } + +} -- cgit v1.2.3