From 0a8d47bec2a232aad6353d34a101eb82c9d4f7ae Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 20 Dec 2016 17:22:21 +1300 Subject: Improved network location providers --- src/org/traccar/Context.java | 13 +- src/org/traccar/LocationProviderHandler.java | 8 +- src/org/traccar/location/BaseLocationProvider.java | 52 -------- src/org/traccar/location/CellInfo.java | 136 --------------------- .../traccar/location/GoogleLocationProvider.java | 26 ++++ src/org/traccar/location/LocationProvider.java | 8 +- .../traccar/location/MozillaLocationProvider.java | 53 +------- .../location/OpenCellIdLocationProvider.java | 56 +++++---- .../location/UniversalLocationProvider.java | 66 ++++++++++ src/org/traccar/model/Position.java | 4 - test/org/traccar/ProtocolTest.java | 18 ++- test/org/traccar/location/CellInfoTest.java | 36 ------ .../org/traccar/location/LocationProviderTest.java | 27 ++-- 13 files changed, 170 insertions(+), 333 deletions(-) delete mode 100644 src/org/traccar/location/BaseLocationProvider.java delete mode 100644 src/org/traccar/location/CellInfo.java create mode 100644 src/org/traccar/location/GoogleLocationProvider.java create mode 100644 src/org/traccar/location/UniversalLocationProvider.java delete mode 100644 test/org/traccar/location/CellInfoTest.java diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 5d6e14efc..14ef85929 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -44,6 +44,7 @@ import org.traccar.geocode.NominatimReverseGeocoder; import org.traccar.geocode.OpenCageReverseGeocoder; import org.traccar.geocode.ReverseGeocoder; import org.traccar.helper.Log; +import org.traccar.location.GoogleLocationProvider; import org.traccar.location.LocationProvider; import org.traccar.location.MozillaLocationProvider; import org.traccar.location.OpenCellIdLocationProvider; @@ -249,20 +250,21 @@ public final class Context { } if (config.getBoolean("location.enable")) { - String type = config.getString("location.type", "opencellid"); + String type = config.getString("location.type", "mozilla"); String key = config.getString("location.key"); switch (type) { - case "mozilla": + case "google": + locationProvider = new GoogleLocationProvider(key); + case "opencellid": + locationProvider = new OpenCellIdLocationProvider(key); + default: if (key != null) { locationProvider = new MozillaLocationProvider(key); } else { locationProvider = new MozillaLocationProvider(); } break; - default: - locationProvider = new OpenCellIdLocationProvider(key); - break; } } @@ -311,6 +313,7 @@ public final class Context { public static void init(IdentityManager testIdentityManager) { config = new Config(); + objectMapper = new ObjectMapper(); identityManager = testIdentityManager; } diff --git a/src/org/traccar/LocationProviderHandler.java b/src/org/traccar/LocationProviderHandler.java index d47f05bd1..5f07358ec 100644 --- a/src/org/traccar/LocationProviderHandler.java +++ b/src/org/traccar/LocationProviderHandler.java @@ -44,15 +44,17 @@ public class LocationProviderHandler implements ChannelUpstreamHandler { Object message = e.getMessage(); if (message instanceof Position) { final Position position = (Position) message; - if (position.getOutdated() || processInvalidPositions && !position.getValid()) { - locationProvider.getLocation(position.getAttributes(), new LocationProvider.LocationProviderCallback() { + if ((position.getOutdated() || processInvalidPositions && !position.getValid()) + && position.getNetwork() != null) { + locationProvider.getLocation(position.getNetwork(), new LocationProvider.LocationProviderCallback() { @Override - public void onSuccess(double latitude, double longitude) { + public void onSuccess(double latitude, double longitude, double accuracy) { position.set(Position.KEY_APPROXIMATE, true); position.setValid(true); position.setFixTime(position.getDeviceTime()); position.setLatitude(latitude); position.setLongitude(longitude); + position.setAccuracy(accuracy); Channels.fireMessageReceived(ctx, position, e.getRemoteAddress()); } diff --git a/src/org/traccar/location/BaseLocationProvider.java b/src/org/traccar/location/BaseLocationProvider.java deleted file mode 100644 index d228685e2..000000000 --- a/src/org/traccar/location/BaseLocationProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2015 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.location; - -import org.traccar.Config; -import org.traccar.Context; -import org.traccar.model.Position; - -import java.util.Map; - -public abstract class BaseLocationProvider implements LocationProvider { - - @Override - public void getLocation(Map attributes, LocationProviderCallback callback) { - - Config config = Context.getConfig(); - - Number mcc = (Number) attributes.get(Position.KEY_MCC); - if (mcc == null && config.hasKey("location.mcc")) { - mcc = config.getInteger("location.mcc"); - } - - Number mnc = (Number) attributes.get(Position.KEY_MNC); - if (mnc == null && config.hasKey("location.mnc")) { - mnc = config.getInteger("location.mnc"); - } - - Number lac = (Number) attributes.get(Position.KEY_LAC); - Number cid = (Number) attributes.get(Position.KEY_CID); - - if (mcc != null && mnc != null && lac != null && cid != null) { - getLocation(mcc.intValue(), mnc.intValue(), lac.longValue(), cid.longValue(), callback); - } - - } - - protected abstract void getLocation(int mcc, int mnc, long lac, long cid, LocationProviderCallback callback); - -} diff --git a/src/org/traccar/location/CellInfo.java b/src/org/traccar/location/CellInfo.java deleted file mode 100644 index 2257cb1e8..000000000 --- a/src/org/traccar/location/CellInfo.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2016 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.location; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.traccar.Config; -import org.traccar.Context; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class CellInfo { - - @JsonInclude(JsonInclude.Include.NON_DEFAULT) - public static class Cell { - - private int mcc; - private int mnc; - private int lac; - private int cid; - private int signal; - - public Cell() { - } - - public Cell(int mcc, int mnc, int lac, int cid, int signal) { - this.mcc = mcc; - this.mnc = mnc; - this.lac = lac; - this.cid = cid; - this.signal = signal; - } - - public int getMcc() { - return mcc; - } - - public void setMcc(int mcc) { - this.mcc = mcc; - } - - public int getMnc() { - return mnc; - } - - public void setMnc(int mnc) { - this.mnc = mnc; - } - - public int getLac() { - return lac; - } - - public void setLac(int lac) { - this.lac = lac; - } - - public int getCid() { - return cid; - } - - public void setCid(int cid) { - this.cid = cid; - } - - public int getSignal() { - return signal; - } - - public void setSignal(int signal) { - this.signal = signal; - } - } - - public CellInfo() { - } - - public CellInfo(Collection cells) { - this.cells.addAll(cells); - } - - private List cells = new ArrayList<>(); - - public List getCells() { - return cells; - } - - public void addCell(int lac, int cid) { - Config config = Context.getConfig(); - if (config.hasKey("location.mcc") && config.hasKey("location.mnc")) { - int mcc = config.getInteger("location.mcc"); - int mnc = config.getInteger("location.mnc"); - cells.add(new Cell(mcc, mnc, lac, cid, 0)); - } - } - - public void addCell(int mcc, int mnc, int lac, int cid) { - cells.add(new Cell(mcc, mnc, lac, cid, 0)); - } - - @Override - public String toString() { - try { - return new ObjectMapper().writeValueAsString(cells); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public static CellInfo fromString(String json) { - try { - return new CellInfo(Arrays.asList(new ObjectMapper().readValue(json, Cell[].class))); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/src/org/traccar/location/GoogleLocationProvider.java b/src/org/traccar/location/GoogleLocationProvider.java new file mode 100644 index 000000000..d4c449170 --- /dev/null +++ b/src/org/traccar/location/GoogleLocationProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 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.location; + +public class GoogleLocationProvider extends UniversalLocationProvider { + + private static final String URL = "https://www.googleapis.com/geolocation/v1/geolocate"; + + public GoogleLocationProvider(String key) { + super(URL, key); + } + +} diff --git a/src/org/traccar/location/LocationProvider.java b/src/org/traccar/location/LocationProvider.java index cc445c2b3..47b9f8909 100644 --- a/src/org/traccar/location/LocationProvider.java +++ b/src/org/traccar/location/LocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2016 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. @@ -15,18 +15,18 @@ */ package org.traccar.location; -import java.util.Map; +import org.traccar.model.Network; public interface LocationProvider { interface LocationProviderCallback { - void onSuccess(double latitude, double longitude); + void onSuccess(double latitude, double longitude, double accuracy); void onFailure(); } - void getLocation(Map attributes, LocationProviderCallback callback); + void getLocation(Network network, LocationProviderCallback callback); } diff --git a/src/org/traccar/location/MozillaLocationProvider.java b/src/org/traccar/location/MozillaLocationProvider.java index cbfc19550..90be7c456 100644 --- a/src/org/traccar/location/MozillaLocationProvider.java +++ b/src/org/traccar/location/MozillaLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2016 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. @@ -15,63 +15,16 @@ */ package org.traccar.location; -import com.ning.http.client.AsyncCompletionHandler; -import com.ning.http.client.Response; -import org.traccar.Context; - -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; - -public class MozillaLocationProvider extends BaseLocationProvider { +public class MozillaLocationProvider extends UniversalLocationProvider { private static final String URL = "https://location.services.mozilla.com/v1/geolocate"; - private String url; - private String template; - public MozillaLocationProvider() { this("test"); } public MozillaLocationProvider(String key) { - this.url = URL + "?key=" + key; - - template = new StringBuilder() - .append("{\"cellTowers\":[{") - .append("\"radioType\":\"gsm\",") - .append("\"mobileCountryCode\":%d,") - .append("\"mobileNetworkCode\":%d,") - .append("\"locationAreaCode\":%d,") - .append("\"cellId\":%d") - .append("}]}") - .toString(); - } - - protected void getLocation(int mcc, int mnc, long lac, long cid, final LocationProviderCallback callback) { - String body = String.format(template, mcc, mnc, lac, cid); - Context.getAsyncHttpClient().preparePost(url).setBody(body).execute(new AsyncCompletionHandler() { - @Override - public Object onCompleted(Response response) throws Exception { - try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { - JsonObject json = reader.readObject().getJsonObject("location"); - if (json.containsKey("lat") && json.containsKey("lon")) { - callback.onSuccess( - json.getJsonNumber("lat").doubleValue(), - json.getJsonNumber("lon").doubleValue()); - } else { - callback.onFailure(); - } - } - return null; - } - - @Override - public void onThrowable(Throwable t) { - callback.onFailure(); - } - }); - + super(URL, key); } } diff --git a/src/org/traccar/location/OpenCellIdLocationProvider.java b/src/org/traccar/location/OpenCellIdLocationProvider.java index d5d1b0ace..b45797b80 100644 --- a/src/org/traccar/location/OpenCellIdLocationProvider.java +++ b/src/org/traccar/location/OpenCellIdLocationProvider.java @@ -18,12 +18,14 @@ package org.traccar.location; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.Response; import org.traccar.Context; +import org.traccar.model.CellTower; +import org.traccar.model.Network; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; -public class OpenCellIdLocationProvider extends BaseLocationProvider { +public class OpenCellIdLocationProvider implements LocationProvider { private String url; @@ -35,29 +37,39 @@ public class OpenCellIdLocationProvider extends BaseLocationProvider { this.url = url + "?format=json&mcc=%d&mnc=%d&lac=%d&cellid=%d&key=" + key; } - protected void getLocation(int mcc, int mnc, long lac, long cid, final LocationProviderCallback callback) { - Context.getAsyncHttpClient().prepareGet(String.format(url, mcc, mnc, lac, cid)) - .execute(new AsyncCompletionHandler() { - @Override - public Object onCompleted(Response response) throws Exception { - try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { - JsonObject json = reader.readObject(); - if (json.containsKey("lat") && json.containsKey("lon")) { - callback.onSuccess( - json.getJsonNumber("lat").doubleValue(), - json.getJsonNumber("lon").doubleValue()); - } else { - callback.onFailure(); + @Override + public void getLocation(Network network, final LocationProviderCallback callback) { + if (network.getCellTowers() != null && !network.getCellTowers().isEmpty()) { + + CellTower cellTower = network.getCellTowers().iterator().next(); + String request = String.format(url, cellTower.getMobileCountryCode(), cellTower.getMobileNetworkCode(), + cellTower.getLocationAreaCode(), cellTower.getCellId()); + + Context.getAsyncHttpClient().prepareGet(request).execute(new AsyncCompletionHandler() { + @Override + public Object onCompleted(Response response) throws Exception { + try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { + JsonObject json = reader.readObject(); + if (json.containsKey("lat") && json.containsKey("lon")) { + callback.onSuccess( + json.getJsonNumber("lat").doubleValue(), + json.getJsonNumber("lon").doubleValue(), 0); + } else { + callback.onFailure(); + } } + return null; } - return null; - } - - @Override - public void onThrowable(Throwable t) { - callback.onFailure(); - } - }); + + @Override + public void onThrowable(Throwable t) { + callback.onFailure(); + } + }); + + } else { + callback.onFailure(); + } } } diff --git a/src/org/traccar/location/UniversalLocationProvider.java b/src/org/traccar/location/UniversalLocationProvider.java new file mode 100644 index 000000000..624d020a5 --- /dev/null +++ b/src/org/traccar/location/UniversalLocationProvider.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 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.location; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.ning.http.client.AsyncCompletionHandler; +import com.ning.http.client.Response; +import org.traccar.Context; +import org.traccar.helper.Log; +import org.traccar.model.Network; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; + +public class UniversalLocationProvider implements LocationProvider { + + private String url; + + public UniversalLocationProvider(String url, String key) { + this.url = url + "?key=" + key; + } + + @Override + public void getLocation(Network network, final LocationProviderCallback callback) { + try { + String request = Context.getObjectMapper().writeValueAsString(network); + Context.getAsyncHttpClient().preparePost(url).setBody(request).execute(new AsyncCompletionHandler() { + @Override + public Object onCompleted(Response response) throws Exception { + try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { + JsonObject json = reader.readObject(); + JsonObject location = json.getJsonObject("location"); + callback.onSuccess( + location.getJsonNumber("lat").doubleValue(), + location.getJsonNumber("lon").doubleValue(), + json.getJsonNumber("accuracy").doubleValue()); + } + return null; + } + + @Override + public void onThrowable(Throwable t) { + callback.onFailure(); + } + }); + } catch (JsonProcessingException e) { + Log.warning(e); + callback.onFailure(); + } + } + +} diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java index fd3e2196c..b43f2455e 100644 --- a/src/org/traccar/model/Position.java +++ b/src/org/traccar/model/Position.java @@ -35,10 +35,6 @@ public class Position extends Message { public static final String KEY_OUTPUT = "output"; public static final String KEY_POWER = "power"; public static final String KEY_BATTERY = "battery"; - public static final String KEY_MCC = "mcc"; - public static final String KEY_MNC = "mnc"; - public static final String KEY_LAC = "lac"; - public static final String KEY_CID = "cid"; public static final String KEY_FUEL = "fuel"; public static final String KEY_FUEL_CONSUMPTION = "fuelConsumption"; public static final String KEY_RFID = "rfid"; diff --git a/test/org/traccar/ProtocolTest.java b/test/org/traccar/ProtocolTest.java index 93f3150c7..43be1a59e 100644 --- a/test/org/traccar/ProtocolTest.java +++ b/test/org/traccar/ProtocolTest.java @@ -7,6 +7,7 @@ import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpVersion; import org.junit.Assert; import org.traccar.database.IdentityManager; +import org.traccar.model.CellTower; import org.traccar.model.Command; import org.traccar.model.Device; import org.traccar.model.Position; @@ -174,16 +175,13 @@ public class ProtocolTest extends BaseTest { Assert.assertFalse("no attributes", attributes.isEmpty()); } - if (attributes.containsKey(Position.KEY_LAC) || attributes.containsKey(Position.KEY_CID)) { - checkInteger(attributes.get(Position.KEY_LAC), 1, 65535); - checkInteger(attributes.get(Position.KEY_CID), 0, 268435455); - } - - if (attributes.containsKey(Position.KEY_MCC) || attributes.containsKey(Position.KEY_MNC)) { - checkInteger(attributes.get(Position.KEY_MCC), 100, 999); - checkInteger(attributes.get(Position.KEY_MNC), 0, 999); - Assert.assertTrue("value missing", attributes.containsKey(Position.KEY_LAC)); - Assert.assertTrue("value missing", attributes.containsKey(Position.KEY_CID)); + if (position.getNetwork() != null) { + for (CellTower cellTower : position.getNetwork().getCellTowers()) { + checkInteger(cellTower.getMobileCountryCode(), 0, 999); + checkInteger(cellTower.getMobileNetworkCode(), 0, 999); + checkInteger(cellTower.getLocationAreaCode(), 1, 65535); + checkInteger(cellTower.getCellId(), 0, 268435455); + } } } diff --git a/test/org/traccar/location/CellInfoTest.java b/test/org/traccar/location/CellInfoTest.java deleted file mode 100644 index e78ce51fb..000000000 --- a/test/org/traccar/location/CellInfoTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.traccar.location; - -import org.junit.Assert; -import org.junit.Test; - -public class CellInfoTest { - - @Test - public void testToString() { - - CellInfo info = new CellInfo(); - info.addCell(0, 0, 1000, 2000); - info.addCell(400, 1, 3000, 4000); - - Assert.assertEquals("[{\"lac\":1000,\"cid\":2000},{\"mcc\":400,\"mnc\":1,\"lac\":3000,\"cid\":4000}]", info.toString()); - - } - - @Test - public void testFromString() { - - CellInfo info = CellInfo.fromString("[{\"lac\":1000,\"cid\":2000}]"); - - Assert.assertEquals(1, info.getCells().size()); - - CellInfo.Cell cell = info.getCells().get(0); - - Assert.assertEquals(0, cell.getMcc()); - Assert.assertEquals(0, cell.getMnc()); - Assert.assertEquals(1000, cell.getLac()); - Assert.assertEquals(2000, cell.getCid()); - Assert.assertEquals(0, cell.getSignal()); - - } - -} diff --git a/test/org/traccar/location/LocationProviderTest.java b/test/org/traccar/location/LocationProviderTest.java index 910f9e9ea..9c6000121 100644 --- a/test/org/traccar/location/LocationProviderTest.java +++ b/test/org/traccar/location/LocationProviderTest.java @@ -2,34 +2,33 @@ package org.traccar.location; import org.junit.Assert; import org.junit.Test; +import org.traccar.BaseTest; +import org.traccar.model.CellTower; +import org.traccar.model.Network; import org.traccar.model.Position; import java.util.HashMap; import java.util.Map; -public class LocationProviderTest { +public class LocationProviderTest extends BaseTest { private boolean enable = false; @Test public void test() { if (enable) { - testOpenCellId(); + testMozilla(); } } - public void testOpenCellId() { - OpenCellIdLocationProvider locationProvider = new OpenCellIdLocationProvider("fake"); + public void testMozilla() { + MozillaLocationProvider locationProvider = new MozillaLocationProvider(); - Map attributes = new HashMap<>(); - attributes.put(Position.KEY_MCC, 260); - attributes.put(Position.KEY_MNC, 2); - attributes.put(Position.KEY_LAC, 10250); - attributes.put(Position.KEY_CID, 26511); + Network network = new Network(CellTower.from(260, 2, 10250, 26511)); - locationProvider.getLocation(attributes, new LocationProvider.LocationProviderCallback() { + locationProvider.getLocation(network, new LocationProvider.LocationProviderCallback() { @Override - public void onSuccess(double latitude, double longitude) { + public void onSuccess(double latitude, double longitude, double accuracy) { Assert.assertEquals(60.07254, latitude, 0.00001); Assert.assertEquals(30.30996, longitude, 0.00001); } @@ -39,6 +38,12 @@ public class LocationProviderTest { Assert.fail(); } }); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } -- cgit v1.2.3