Skip to content

Commit

Permalink
For #193 - added basic support for 'snap to roads' function
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalidze committed Jul 13, 2015
1 parent e2fec5b commit 969562c
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public void onSelected(Position position) {
}

@Override
public void onLoad(final Device device, Date from, Date to, boolean filter, final ArchiveStyle style) {
public void onLoad(final Device device, Date from, Date to, boolean filter, boolean snapToRoads, final ArchiveStyle style) {
if (device != null && from != null && to != null) {
Application.getDataService().getPositions(device, from, to, filter, new BaseAsyncCallback<List<Position>>(i18n) {
Application.getDataService().getPositions(device, from, to, filter, snapToRoads, new BaseAsyncCallback<List<Position>>(i18n) {
@Override
public void onSuccess(List<Position> result) {
archiveHandler.onClear(device);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/traccar/web/client/i18n/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,6 @@ String defaultNotificationTemplate(@Select DeviceEventType type,
String overlay();

String overlayType(@Select UserSettings.OverlayType type);

String snapToRoads();
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public interface DataService extends RemoteService {

void saveDeviceShare(Device device, Map<User, Boolean> share);

List<Position> getPositions(Device device, Date from, Date to, boolean filter);
List<Position> getPositions(Device device, Date from, Date to, boolean filter, boolean snapToRoads);

List<Position> getLatestPositions();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public interface DataServiceAsync {

void getLatestPositions(AsyncCallback<List<Position>> callback);

void getPositions(Device device, Date from, Date to, boolean filter, AsyncCallback<List<Position>> callback);
void getPositions(Device device, Date from, Date to, boolean filter, boolean snapToRoads, AsyncCallback<List<Position>> callback);

void updateApplicationSettings(ApplicationSettings applicationSettings, AsyncCallback<Void> callback);

Expand Down
12 changes: 9 additions & 3 deletions src/main/java/org/traccar/web/client/view/ArchiveView.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface ArchiveViewUiBinder extends UiBinder<Widget, ArchiveView> {

public interface ArchiveHandler {
void onSelected(Position position);
void onLoad(Device device, Date from, Date to, boolean filter, ArchiveStyle style);
void onLoad(Device device, Date from, Date to, boolean filter, boolean snapToRoads, ArchiveStyle style);
void onFilterSettings();
void onClear(Device device);
void onChangeArchiveMarkerType(PositionIconType newMarkerType);
Expand Down Expand Up @@ -100,6 +100,9 @@ public ContentPanel getView() {
@UiField
CheckBox disableFilter;

@UiField
CheckBox snapToRoads;

@UiField(provided = true)
TextButton styleButtonTrackColor;

Expand Down Expand Up @@ -235,6 +238,7 @@ public void onLoadClicked(SelectEvent event) {
getCombineDate(fromDate, fromTime),
getCombineDate(toDate, toTime),
!disableFilter.getValue(),
snapToRoads.getValue(),
new ArchiveStyle(style)
);
}
Expand All @@ -260,7 +264,8 @@ public void onCSVClicked(SelectionEvent<Item> event) {
"?deviceId=" + (deviceCombo.getValue() == null ? null : deviceCombo.getValue().getId()) +
"&from=" + jsonTimeFormat.format(getCombineDate(fromDate, fromTime)).replaceFirst("\\+", "%2B") +
"&to=" + jsonTimeFormat.format(getCombineDate(toDate, toTime)).replaceFirst("\\+", "%2B") +
"&filter=" + !disableFilter.getValue(),
"&filter=" + !disableFilter.getValue() +
"&snapToRoads=" + snapToRoads.getValue(),
"_blank", null);
}
}
Expand All @@ -276,7 +281,8 @@ public void onGPXClicked(SelectionEvent<Item> event) {
"?deviceId=" + (deviceCombo.getValue() == null ? null : deviceCombo.getValue().getId()) +
"&from=" + jsonTimeFormat.format(getCombineDate(fromDate, fromTime)).replaceFirst("\\+", "%2B") +
"&to=" + jsonTimeFormat.format(getCombineDate(toDate, toTime)).replaceFirst("\\+", "%2B") +
"&filter=" + !disableFilter.getValue(),
"&filter=" + !disableFilter.getValue() +
"&snapToRoads=" + snapToRoads.getValue(),
"_blank", null);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/traccar/web/client/view/ArchiveView.ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<button:TextButton ui:field="filterButton" text="{i18n.filter}..." />
<toolbar:LabelToolItem width="2" />
<form:CheckBox ui:field="disableFilter" boxLabel="{i18n.disableFilter}" />
<toolbar:LabelToolItem width="2" />
<form:CheckBox ui:field="snapToRoads" boxLabel="{i18n.snapToRoads}" />

<toolbar:ToolBar ui:field="styleToolbar">
<button:TextButton ui:field="styleButtonTrackColor" text=" " height="80%"/>
Expand Down
68 changes: 66 additions & 2 deletions src/main/java/org/traccar/web/server/model/DataServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
package org.traccar.web.server.model;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.NumberFormat;
import java.util.*;
import java.util.List;

Expand All @@ -29,9 +33,14 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.inject.persist.Transactional;

import org.apache.commons.io.IOUtils;
import org.hibernate.proxy.HibernateProxy;
import org.traccar.web.client.model.DataService;
import org.traccar.web.client.model.EventService;
Expand Down Expand Up @@ -575,7 +584,7 @@ public Device removeDevice(Device device) {
@Transactional
@RequireUser
@Override
public List<Position> getPositions(Device device, Date from, Date to, boolean filter) {
public List<Position> getPositions(Device device, Date from, Date to, boolean filter, boolean snapToRoads) {
EntityManager entityManager = getSessionEntityManager();
UserSettings filters = getSessionUser().getUserSettings();

Expand Down Expand Up @@ -626,7 +635,62 @@ public List<Position> getPositions(Device device, Date from, Date to, boolean fi
}
if (add) positions.add(queryResult.get(i));
}
return new ArrayList<Position>(positions);

positions = new ArrayList<Position>(positions);
if (snapToRoads && !queryResult.isEmpty()) {
NumberFormat lonLatFormat = NumberFormat.getNumberInstance(Locale.US);
lonLatFormat.setMinimumFractionDigits(6);
lonLatFormat.setMaximumFractionDigits(6);

StringBuilder matchURL = new StringBuilder("http://router.project-osrm.org/match?geometry=false");
for (Position position : positions) {
matchURL.append("&loc=").append(lonLatFormat.format(position.getLatitude()))
.append(',').append(lonLatFormat.format(position.getLongitude()))
.append("&t=").append(position.getTime().getTime() / 1000);
}

InputStream is = null;
URLConnection conn = null;
try {
URL url = new URL(matchURL.toString());
conn = url.openConnection();
conn.setRequestProperty("User-Agent", "Traccar Web UI Mod (java)");
is = conn.getInputStream();

JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
Gson gson = new Gson();
reader.beginObject();

String name = reader.nextName();
if (name.equals("matchings")) {
reader.beginArray();
while (reader.hasNext()) {
JsonObject matching = gson.fromJson(reader, JsonObject.class);
JsonArray indices = matching.getAsJsonArray("indices");
JsonArray matched_points = matching.getAsJsonArray("matched_points");
for (int i = 0; i < indices.size(); i++) {
JsonArray latLon = matched_points.get(i).getAsJsonArray();
int index = indices.get(i).getAsInt();
Position snapped = new Position(positions.get(index));
snapped.setLatitude(latLon.get(0).getAsDouble());
snapped.setLongitude(latLon.get(1).getAsDouble());
positions.set(index, snapped);
}
}
reader.endArray();
}
reader.endObject();
reader.close();
} catch (MalformedURLException mfe) {
log("Incorrect URL", mfe);
} catch (IOException ioex) {
log("I/O error", ioex);
} finally {
IOUtils.closeQuietly(is);
}
}

return positions;
}

@RequireUser
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/org/traccar/web/server/model/ExportServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,16 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
Date to = requestDateFormat.parse(req.getParameter("to"));
String strFilter = req.getParameter("filter");
boolean filter = strFilter != null && strFilter.equalsIgnoreCase("true");
String strSnapToRoads = req.getParameter("snapToRoads");
boolean snapToRoads = strSnapToRoads != null && strSnapToRoads.equalsIgnoreCase("true");

Device device = entityManager.get().find(Device.class, deviceId);
checkAccess(device);

if (exportType.equals("csv")) {
csv(resp, device, from, to, filter);
csv(resp, device, from, to, filter, snapToRoads);
} else if (exportType.equals("gpx")) {
gpx(resp, device, from, to, filter);
gpx(resp, device, from, to, filter, snapToRoads);
} else {
throw new ServletException("Unsupported export type: " + exportType);
}
Expand All @@ -98,7 +100,7 @@ void checkAccess(Device device) {
}
}

void csv(HttpServletResponse response, Device device, Date from, Date to, boolean filter) throws IOException {
void csv(HttpServletResponse response, Device device, Date from, Date to, boolean filter, boolean snapToRoads) throws IOException {
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=traccar-positions.csv");

Expand All @@ -108,7 +110,7 @@ void csv(HttpServletResponse response, Device device, Date from, Date to, boolea

writer.println(line(SEPARATOR, "time", "valid", "latitude", "longitude", "altitude", "speed", "distance", "course", "power", "address", "other"));

for (Position p : dataService.getPositions(device, from, to, filter)) {
for (Position p : dataService.getPositions(device, from, to, filter, snapToRoads)) {
writer.println(line(SEPARATOR, p.getTime(), p.getValid(), p.getLatitude(), p.getLongitude(), p.getAltitude(), p.getSpeed(), p.getDistance(), p.getCourse(), p.getPower(), p.getAddress(), p.getOther()));
}
}
Expand All @@ -125,7 +127,7 @@ private static String line(char SEPARATOR, Object... s) {
return result.toString();
}

void gpx(HttpServletResponse response, Device device, Date from, Date to, boolean filter) throws IOException, XMLStreamException {
void gpx(HttpServletResponse response, Device device, Date from, Date to, boolean filter, boolean snapToRoads) throws IOException, XMLStreamException {
response.setContentType("application/gpx+xml;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=traccar-positions.gpx");

Expand Down Expand Up @@ -169,7 +171,7 @@ void gpx(HttpServletResponse response, Device device, Date from, Date to, boolea
xsw.writeCharacters("Traccar archive");
xsw.writeEndElement();
xsw.writeStartElement("trkseg");
for (Position p : dataService.getPositions(device, from, to, filter)) {
for (Position p : dataService.getPositions(device, from, to, filter, snapToRoads)) {
xsw.writeStartElement("trkpt");
xsw.writeAttribute("lat", p.getLatitude().toString());
xsw.writeAttribute("lon", p.getLongitude().toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ defaultHashImplementation = Default password hash
archive = Archive
from = From
to = To
snapToRoads = Snap to roads
load = Load
exportToCSV = Export to CSV
exportToGPX = Export to GPX
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ defaultHashImplementation = Хэш-функция паролей
archive=Архив
from=С
to=По
snapToRoads = Привязка к дорогам
errNoResults = В выбранном периоде нет записей
errFillFields = Все поля должны быть заполнены
errNoDeviceNameOrId = Пожалуйста введите имя и уникальный код
Expand Down

0 comments on commit 969562c

Please sign in to comment.