Skip to content

Commit

Permalink
Updated import, added test
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalidze committed Feb 18, 2015
1 parent ee82e49 commit a379875
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 75 deletions.
130 changes: 130 additions & 0 deletions src/main/java/org/traccar/web/server/model/GPXParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2015 Vitaly Litvak ([email protected])
*
* 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.web.server.model;

import org.apache.commons.lang3.StringEscapeUtils;
import org.traccar.web.shared.model.Device;
import org.traccar.web.shared.model.Position;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class GPXParser {
public static class Result {
Position latestPosition;
List<Position> positions;
}

public Result parse(InputStream inputStream, Device device) throws XMLStreamException, ParseException {
Result result = new Result();

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
DateFormat dateFormatWithMS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

dateFormat.setTimeZone(tz);
dateFormatWithMS.setTimeZone(tz);

XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(inputStream);

result.positions = new LinkedList<Position>();
Position position = null;
Stack<String> extensionsElements = new Stack<String>();
boolean extensionsStarted = false;

while (xsr.hasNext()) {
xsr.next();
if (xsr.getEventType() == XMLStreamReader.START_ELEMENT) {
if (xsr.getLocalName().equalsIgnoreCase("trkpt")) {
position = new Position();
position.setLongitude(Double.parseDouble(xsr.getAttributeValue(null, "lon")));
position.setLatitude(Double.parseDouble(xsr.getAttributeValue(null, "lat")));
position.setValid(Boolean.TRUE);
position.setDevice(device);
} else if (xsr.getLocalName().equalsIgnoreCase("time")) {
if (position != null) {
String strTime = xsr.getElementText();
if (strTime.length() == 20) {
position.setTime(dateFormat.parse(strTime));
} else {
position.setTime(dateFormatWithMS.parse(strTime));
}
}
} else if (xsr.getLocalName().equalsIgnoreCase("ele") && position != null) {
position.setAltitude(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("address") && position != null) {
position.setAddress(StringEscapeUtils.unescapeXml(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("speed") && position != null) {
position.setSpeed(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("power") && position != null) {
position.setPower(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("course") && position != null) {
position.setCourse(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("other") && position != null) {
position.setOther(StringEscapeUtils.unescapeXml(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("extensions")) {
extensionsStarted = true;
} else if (position != null && extensionsStarted) {
extensionsElements.push(xsr.getLocalName());
}
} else if (xsr.getEventType() == XMLStreamReader.END_ELEMENT) {
if (xsr.getLocalName().equalsIgnoreCase("trkpt")) {
if (position.getOther() == null) {
position.setOther("<info><protocol>gpx_import</protocol></info>");
}

if (position.getOther().endsWith("</info>")) {
position.setOther(position.getOther().substring(0, position.getOther().length() - 7));
} else {
position.setOther("<info><protocol>gpx_import</protocol>" + position.getOther());
}
position.setOther(position.getOther() + "<type>" +
(result.positions.isEmpty() ? "import_start" : "import") + "</type></info>");

result.positions.add(position);
if (result.latestPosition == null || result.latestPosition.getTime().compareTo(position.getTime()) < 0) {
result.latestPosition = position;
}
position = null;
} else if (xsr.getLocalName().equalsIgnoreCase("extensions")) {
extensionsStarted = false;
} else if (extensionsStarted) {
extensionsElements.pop();
}
} else if (extensionsStarted && xsr.getEventType() == XMLStreamReader.CHARACTERS && !xsr.getText().trim().isEmpty() && !extensionsElements.empty()) {
String name = "";
for (int i = 0; i < extensionsElements.size(); i++) {
name += (name.length() > 0 ? "-" : "") + extensionsElements.get(i);
}
position.setOther((position.getOther() == null ? "" : position.getOther()) +
"<" + name + ">" + xsr.getText() + "</" + name + ">");
}
}

if (result.positions.size() > 1) {
Position last = ((LinkedList<Position>) result.positions).getLast();
last.setOther(last.getOther().replaceFirst("<type>import</type>", "<type>import_end</type>"));
}

return result;
}
}
80 changes: 5 additions & 75 deletions src/main/java/org/traccar/web/server/model/ImportServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringEscapeUtils;
import org.traccar.web.shared.model.ApplicationSettings;
import org.traccar.web.shared.model.Device;
import org.traccar.web.shared.model.Position;
Expand All @@ -33,9 +32,7 @@
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
Expand Down Expand Up @@ -107,80 +104,13 @@ void gpx(Device device, InputStream inputStream, HttpServletResponse response) t


try {
XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(inputStream);

List<Position> parsedPositions = new LinkedList<Position>();
Position position = null;
Position latestPosition = null;
GPXParser.Result parsed = new GPXParser().parse(inputStream, device);

response.getWriter().println("<pre>");

int imported = 0;

while (xsr.hasNext()) {
xsr.next();
if (xsr.getEventType() == XMLStreamReader.START_ELEMENT) {

if (xsr.getLocalName().equalsIgnoreCase("trkpt")) {
position = new Position();
position.setLongitude(Double.parseDouble(xsr.getAttributeValue(null, "lon")));
position.setLatitude(Double.parseDouble(xsr.getAttributeValue(null, "lat")));
position.setValid(Boolean.TRUE);
position.setDevice(device);
} else if (xsr.getLocalName().equalsIgnoreCase("time")) {
if (position != null) {
String strTime = xsr.getElementText();
if (strTime.length() == 20) {
position.setTime(dateFormat.parse(strTime));
} else {
position.setTime(dateFormatWithMS.parse(strTime));
}
}
} else if (xsr.getLocalName().equalsIgnoreCase("ele") && position != null) {
position.setAltitude(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("address") && position != null) {
position.setAddress(StringEscapeUtils.unescapeXml(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("speed") && position != null) {
position.setSpeed(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("power") && position != null) {
position.setPower(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("course") && position != null) {
position.setCourse(Double.parseDouble(xsr.getElementText()));
} else if (xsr.getLocalName().equalsIgnoreCase("other") && position != null) {
position.setOther(StringEscapeUtils.unescapeXml(xsr.getElementText()));
} else if (!xsr.getLocalName().equalsIgnoreCase("extensions") && position != null) {
position.setOther((position.getOther() == null ? "" : position.getOther()) +
"<" + xsr.getLocalName() + ">" + xsr.getElementText() + "</" + xsr.getLocalName() + ">");
}
} else if (xsr.getEventType() == XMLStreamReader.END_ELEMENT &&
xsr.getLocalName().equalsIgnoreCase("trkpt")) {

parsedPositions.add(position);
if (latestPosition == null || position.getTime().compareTo(position.getTime()) < 0) {
latestPosition = position;
}
position = null;
}
}

for (int i = 0; i < parsedPositions.size(); i++) {
position = parsedPositions.get(i);
StringBuilder other = new StringBuilder("<info><protocol>gpx_import</protocol>");
other.append("<type>");
if (i == 0) {
other.append("import_start");
} else if (i == parsedPositions.size() - 1) {
other.append("import_end");
} else {
other.append("import");
}
other.append("</type>");
if (position.getOther() != null) {
other.append(position.getOther());
}
other.append("</info>");
position.setOther(other.toString());

for (Position position : parsed.positions) {
boolean exist = false;
for (Position existing : entityManager.get().createQuery("SELECT p FROM Position p WHERE p.device=:device AND p.time=:time", Position.class)
.setParameter("device", device)
Expand All @@ -200,11 +130,11 @@ void gpx(Device device, InputStream inputStream, HttpServletResponse response) t
}
}

if (latestPosition != null && device.getLatestPosition() == null || device.getLatestPosition().getTime().compareTo(latestPosition.getTime()) < 0) {
device.setLatestPosition(latestPosition);
if (parsed.latestPosition != null && device.getLatestPosition() == null || device.getLatestPosition().getTime().compareTo(parsed.latestPosition.getTime()) < 0) {
device.setLatestPosition(parsed.latestPosition);
}

response.getWriter().println("Already exist: " + (parsedPositions.size() - imported));
response.getWriter().println("Already exist: " + (parsed.positions.size() - imported));
response.getWriter().println("Imported: " + imported);

response.getWriter().println("</pre>");
Expand Down
104 changes: 104 additions & 0 deletions src/test/java/org/traccar/web/server/model/GPXParserTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2015 Vitaly Litvak ([email protected])
*
* 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.web.server.model;

import org.junit.Test;
import org.traccar.web.shared.model.Position;

import javax.xml.stream.XMLStreamException;
import java.text.ParseException;

import static org.junit.Assert.*;

public class GPXParserTest {
@Test
public void testTraccarOutput() throws XMLStreamException, ParseException {
GPXParser.Result r = new GPXParser().parse(getClass().getResourceAsStream("/org/traccar/web/server/model/test_traccar.gpx"), null);
assertNotNull(r);
assertNotNull(r.positions);
assertEquals(2, r.positions.size());

Position p = r.positions.get(0);
assertEquals(-1, p.getLatitude(), 0.01);
assertEquals(-3, p.getLongitude(), 0.01);
assertEquals("ADDR1", p.getAddress());
assertEquals(5.5, p.getSpeed(), 0.01);
assertEquals(7.7, p.getCourse(), 0.01);
assertEquals(55.5, p.getAltitude(), 0.01);
assertEquals(1420096479000L, p.getTime().getTime());
assertEquals("<info><protocol>gps103</protocol><alarm>acc on</alarm><type>import_start</type></info>", p.getOther());

p = r.positions.get(1);
assertEquals(-2, p.getLatitude(), 0.01);
assertEquals(-4, p.getLongitude(), 0.01);
assertEquals("ADDR2", p.getAddress());
assertEquals(8.8, p.getSpeed(), 0.01);
assertEquals(10.10, p.getCourse(), 0.01);
assertEquals(77.7, p.getAltitude(), 0.01);
assertEquals(1420102196000L, p.getTime().getTime());
assertEquals("<info><protocol>gps103</protocol><alarm>acc on</alarm><type>import_end</type></info>", p.getOther());

assertNotNull(r.latestPosition);
assertTrue(r.latestPosition == r.positions.get(1));
}

@Test
public void testForeign() throws XMLStreamException, ParseException {
GPXParser.Result r = new GPXParser().parse(getClass().getResourceAsStream("/org/traccar/web/server/model/test_foreign.gpx"), null);
assertNotNull(r);
assertNotNull(r.positions);
assertEquals(6, r.positions.size());

Object[][] expected = new Object[][] {
{ 10.02591601, 11.01236986, 109.43, 1404758632655L },
{ 12.0259547, 13.01249098, 104.76, 1404758644193L },
{ 14.02606332, 15.01255586, 105.04, 1404758649182L },
{ 16.02613562, 17.01263875, 107.89, 1404758652179L },
{ 18.02623771, 19.01269516, 108.71, 1404758656194L },
{ 20.02631864, 21.01279424, 108.77, 1404758660181L } };

for (int i = 0; i < r.positions.size(); i++) {
Position p = r.positions.get(i);
assertEquals((Double) expected[i][0], p.getLatitude(), 0.00000000001);
assertEquals((Double) expected[i][1], p.getLongitude(), 0.00000000001);
assertEquals((Double) expected[i][2], p.getAltitude(), 0.0001);
assertEquals(((Long) expected[i][3]).longValue(), p.getTime().getTime());
assertNull(p.getAddress());
assertNull(p.getSpeed());
assertNull(p.getCourse());
assertEquals("<info><protocol>gpx_import</protocol><type>import" + (i == 0 ? "_start" : i == r.positions.size() - 1 ? "_end" : "") + "</type></info>", p.getOther());
}

assertNotNull(r.latestPosition);
assertTrue(r.latestPosition == r.positions.get(5));
}

@Test
public void testWithForeignExtensions() throws XMLStreamException, ParseException {
GPXParser.Result r = new GPXParser().parse(getClass().getResourceAsStream("/org/traccar/web/server/model/test_foreign_ext.gpx"), null);
assertEquals(1, r.positions.size());
Position p = r.positions.get(0);
assertEquals(10.02591601, p.getLatitude(), 0.00000000001);
assertEquals(11.01236986, p.getLongitude(), 0.00000000001);
assertEquals(109.43, p.getAltitude(), 0.0001);
assertEquals(1404758632655L, p.getTime().getTime());
assertNull(p.getAddress());
assertNull(p.getCourse());
assertNull(p.getPower());
assertNull(p.getSpeed());
assertEquals("<info><protocol>gpx_import</protocol><Primary_ID>PID4</Primary_ID><Secondary_ID>SID4</Secondary_ID><additional-a>X</additional-a><additional-b>Y</additional-b><type>import_start</type></info>", p.getOther());
}
}
35 changes: 35 additions & 0 deletions src/test/resources/org/traccar/web/server/model/test_foreign.gpx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1"
creator="GlobalMotion" version="1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<trk>
<number>1</number>
<trkseg>
<trkpt lat="10.02591601" lon="11.01236986">
<ele>109.43</ele>
<time>2014-07-07T18:43:52.655Z</time>
</trkpt>
<trkpt lat="12.02595470" lon="13.01249098">
<ele>104.76</ele>
<time>2014-07-07T18:44:04.193Z</time>
</trkpt>
<trkpt lat="14.02606332" lon="15.01255586">
<ele>105.04</ele>
<time>2014-07-07T18:44:09.182Z</time>
</trkpt>
<trkpt lat="16.02613562" lon="17.01263875">
<ele>107.89</ele>
<time>2014-07-07T18:44:12.179Z</time>
</trkpt>
<trkpt lat="18.02623771" lon="19.01269516">
<ele>108.71</ele>
<time>2014-07-07T18:44:16.194Z</time>
</trkpt>
<trkpt lat="20.02631864" lon="21.01279424">
<ele>108.77</ele>
<time>2014-07-07T18:44:20.181Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
Loading

0 comments on commit a379875

Please sign in to comment.