aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/org/traccar/api/resource/ReportResource.java27
-rw-r--r--src/org/traccar/reports/ReportUtils.java190
-rw-r--r--src/org/traccar/reports/Stops.java101
-rw-r--r--src/org/traccar/reports/Trips.java136
-rw-r--r--src/org/traccar/reports/model/BaseReport.java10
-rw-r--r--src/org/traccar/reports/model/StopReport.java122
-rw-r--r--src/org/traccar/reports/model/SummaryReport.java15
-rw-r--r--src/org/traccar/reports/model/TripReport.java11
-rw-r--r--src/org/traccar/reports/model/TripsConfig.java71
-rw-r--r--templates/export/stops.xlsxbin0 -> 12855 bytes
-rw-r--r--test/org/traccar/reports/TripsAndStopsTest.java199
-rw-r--r--test/org/traccar/reports/TripsTest.java70
12 files changed, 729 insertions, 223 deletions
diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/org/traccar/api/resource/ReportResource.java
index a0f686e80..7c472e84d 100644
--- a/src/org/traccar/api/resource/ReportResource.java
+++ b/src/org/traccar/api/resource/ReportResource.java
@@ -22,9 +22,11 @@ import org.traccar.model.Position;
import org.traccar.reports.Events;
import org.traccar.reports.Summary;
import org.traccar.reports.Trips;
+import org.traccar.reports.model.StopReport;
import org.traccar.reports.model.SummaryReport;
import org.traccar.reports.model.TripReport;
import org.traccar.reports.Route;
+import org.traccar.reports.Stops;
@Path("reports")
@Produces(MediaType.APPLICATION_JSON)
@@ -129,4 +131,29 @@ public class ReportResource extends BaseResource {
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
}
+ @Path("stops")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Collection<StopReport> getStops(
+ @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
+ @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
+ return Stops.getObjects(getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ }
+
+ @Path("stops")
+ @GET
+ @Produces(XLSX)
+ public Response getStopsExcel(
+ @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
+ @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Stops.getExcel(stream, getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+
+ return Response.ok(stream.toByteArray())
+ .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ }
+
+
}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index 84d3b3a77..252e7caad 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,10 @@ import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.TransformerFactory;
import org.traccar.Context;
import org.traccar.model.Position;
+import org.traccar.reports.model.BaseReport;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
import java.io.IOException;
import java.io.InputStream;
@@ -123,4 +127,186 @@ public final class ReportUtils {
transformer.write();
}
+ public static TripsConfig initTripsConfig() {
+ return new TripsConfig(
+ Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000,
+ Context.getConfig().getLong("report.trip.minimalTripDistance", 500),
+ Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000,
+ Context.getConfig().getBoolean("report.trip.greedyParking"));
+ }
+
+ private static TripReport calculateTrip(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+ Position startTrip = positions.get(startIndex);
+ Position endTrip = positions.get(endIndex);
+
+ double speedMax = 0.0;
+ double speedSum = 0.0;
+ for (int i = startIndex; i <= endIndex; i++) {
+ double speed = positions.get(i).getSpeed();
+ speedSum += speed;
+ if (speed > speedMax) {
+ speedMax = speed;
+ }
+ }
+
+ TripReport trip = new TripReport();
+
+ long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime();
+ long deviceId = startTrip.getDeviceId();
+ trip.setDeviceId(deviceId);
+ trip.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
+
+ trip.setStartPositionId(startTrip.getId());
+ trip.setStartLat(startTrip.getLatitude());
+ trip.setStartLon(startTrip.getLongitude());
+ trip.setStartTime(startTrip.getFixTime());
+ trip.setStartAddress(startTrip.getAddress());
+
+ trip.setEndPositionId(endTrip.getId());
+ trip.setEndLat(endTrip.getLatitude());
+ trip.setEndLon(endTrip.getLongitude());
+ trip.setEndTime(endTrip.getFixTime());
+ trip.setEndAddress(endTrip.getAddress());
+
+ trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer));
+ trip.setDuration(tripDuration);
+ trip.setAverageSpeed(speedSum / (endIndex - startIndex));
+ trip.setMaxSpeed(speedMax);
+ trip.setSpentFuel(calculateFuel(startTrip, endTrip));
+
+ return trip;
+ }
+
+ private static StopReport calculateStop(ArrayList<Position> positions, int startIndex, int endIndex) {
+ Position startStop = positions.get(startIndex);
+ Position endStop = positions.get(endIndex);
+
+ StopReport stop = new StopReport();
+
+ long deviceId = startStop.getDeviceId();
+ stop.setDeviceId(deviceId);
+ stop.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
+
+ stop.setPositionId(startStop.getId());
+ stop.setLatitude(startStop.getLatitude());
+ stop.setLongitude(startStop.getLongitude());
+ stop.setStartTime(startStop.getFixTime());
+ stop.setAddress(startStop.getAddress());
+ stop.setEndTime(endStop.getFixTime());
+
+ long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
+ stop.setDuration(stopDuration);
+ stop.setSpentFuel(calculateFuel(startStop, endStop));
+
+ long engineHours = 0;
+ for (int i = startIndex + 1; i <= endIndex; i++) {
+ if (positions.get(i).getBoolean(Position.KEY_IGNITION)
+ && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) {
+ engineHours += positions.get(i).getFixTime().getTime() - positions.get(i - 1).getFixTime().getTime();
+ }
+ }
+ stop.setEngineHours(engineHours);
+
+ return stop;
+
+ }
+
+ public static Collection<BaseReport> detectTripsAndStops(TripsConfig tripsConfig,
+ boolean ignoreOdometer, double speedThreshold,
+ Collection<Position> positionCollection, boolean trips) {
+
+ Collection<BaseReport> result = new ArrayList<>();
+
+ ArrayList<Position> positions = new ArrayList<>(positionCollection);
+ if (positions != null && !positions.isEmpty()) {
+ int previousStartParkingIndex = 0;
+ int startParkingIndex = -1;
+ int previousEndParkingIndex = 0;
+ int endParkingIndex = 0;
+
+ boolean isMoving = false;
+ boolean isLast = false;
+ boolean skipped = false;
+ boolean tripFiltered = false;
+
+ for (int i = 0; i < positions.size(); i++) {
+ isMoving = positions.get(i).getSpeed() > speedThreshold;
+ isLast = i == positions.size() - 1;
+
+ if ((isMoving || isLast) && startParkingIndex != -1) {
+ if (!skipped || previousEndParkingIndex == 0) {
+ previousEndParkingIndex = endParkingIndex;
+ }
+ endParkingIndex = i;
+ }
+ if (!isMoving && startParkingIndex == -1) {
+ if (tripsConfig.getGreedyParking()) {
+ long tripDuration = positions.get(i).getFixTime().getTime()
+ - positions.get(endParkingIndex).getFixTime().getTime();
+ double tripDistance = ReportUtils.calculateDistance(positions.get(endParkingIndex),
+ positions.get(i), false);
+ tripFiltered = tripDuration < tripsConfig.getMinimalTripDuration()
+ && tripDistance < tripsConfig.getMinimalTripDistance();
+ if (tripFiltered) {
+ startParkingIndex = previousStartParkingIndex;
+ endParkingIndex = previousEndParkingIndex;
+ tripFiltered = false;
+ } else {
+ previousStartParkingIndex = i;
+ startParkingIndex = i;
+ }
+ } else {
+ long tripDuration = positions.get(i).getFixTime().getTime()
+ - positions.get(previousEndParkingIndex).getFixTime().getTime();
+ double tripDistance = ReportUtils.calculateDistance(positions.get(previousEndParkingIndex),
+ positions.get(i), false);
+ tripFiltered = tripDuration < tripsConfig.getMinimalTripDuration()
+ && tripDistance < tripsConfig.getMinimalTripDistance();
+ startParkingIndex = i;
+ }
+ }
+ if (startParkingIndex != -1 && (endParkingIndex > startParkingIndex || isLast)) {
+ long parkingDuration = positions.get(endParkingIndex).getFixTime().getTime()
+ - positions.get(startParkingIndex).getFixTime().getTime();
+ if ((parkingDuration >= tripsConfig.getMinimalParkingDuration() || isLast)
+ && previousEndParkingIndex < startParkingIndex) {
+ if (!tripFiltered) {
+ if (trips) {
+ result.add(calculateTrip(
+ positions, previousEndParkingIndex, startParkingIndex, ignoreOdometer));
+ } else {
+ if (result.isEmpty() && previousEndParkingIndex > previousStartParkingIndex) {
+ long previousParkingDuration = positions.get(previousEndParkingIndex)
+ .getFixTime().getTime() - positions.get(previousStartParkingIndex)
+ .getFixTime().getTime();
+ if (previousParkingDuration >= tripsConfig.getMinimalParkingDuration()) {
+ result.add(calculateStop(positions, previousStartParkingIndex,
+ previousEndParkingIndex));
+ }
+ }
+ result.add(calculateStop(positions, startParkingIndex, endParkingIndex));
+ }
+ }
+ previousEndParkingIndex = endParkingIndex;
+ skipped = false;
+ } else {
+ skipped = true;
+ }
+ startParkingIndex = -1;
+ }
+ }
+ if (result.isEmpty() && !trips) {
+ int end = isMoving && !tripsConfig.getGreedyParking()
+ ? Math.max(endParkingIndex, previousEndParkingIndex) : positions.size() - 1;
+ long parkingDuration = positions.get(end).getFixTime().getTime()
+ - positions.get(previousStartParkingIndex).getFixTime().getTime();
+ if (parkingDuration >= tripsConfig.getMinimalParkingDuration()) {
+ result.add(calculateStop(positions, previousStartParkingIndex, end));
+ }
+ }
+ }
+
+ return result;
+ }
}
diff --git a/src/org/traccar/reports/Stops.java b/src/org/traccar/reports/Stops.java
new file mode 100644
index 000000000..64e589be1
--- /dev/null
+++ b/src/org/traccar/reports/Stops.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.reports;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.Context;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.reports.model.BaseReport;
+import org.traccar.reports.model.DeviceReport;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripsConfig;
+
+public final class Stops {
+
+ private Stops() {
+ }
+
+ private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws SQLException {
+ double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
+
+ TripsConfig tripsConfig = ReportUtils.initTripsConfig();
+
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig,
+ ignoreOdometer, speedThreshold,
+ Context.getDataManager().getPositions(deviceId, from, to), false);
+
+ return (Collection<StopReport>) result;
+ }
+
+ public static Collection<StopReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ ArrayList<StopReport> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ result.addAll(detectStops(deviceId, from, to));
+ }
+ return result;
+ }
+
+ public static void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException, IOException {
+ ArrayList<DeviceReport> devicesStops = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<StopReport> stops = detectStops(deviceId, from, to);
+ DeviceReport deviceStops = new DeviceReport();
+ Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ deviceStops.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName()));
+ if (device.getGroupId() != 0) {
+ Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
+ if (group != null) {
+ deviceStops.setGroupName(group.getName());
+ }
+ }
+ deviceStops.setObjects(stops);
+ devicesStops.add(deviceStops);
+ }
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/stops.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("devices", devicesStops);
+ jxlsContext.putVar("sheetNames", sheetNames);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
+ }
+ }
+
+}
diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
index 966c3a817..5c26bea54 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/org/traccar/reports/Trips.java
@@ -29,147 +29,29 @@ import org.apache.poi.ss.util.WorkbookUtil;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Group;
-import org.traccar.model.Position;
+import org.traccar.reports.model.BaseReport;
import org.traccar.reports.model.DeviceReport;
import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
public final class Trips {
private Trips() {
}
- private static TripReport calculateTrip(
- ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
- Position startTrip = positions.get(startIndex);
- Position endTrip = positions.get(endIndex);
-
- double speedMax = 0.0;
- double speedSum = 0.0;
- for (int i = startIndex; i <= endIndex; i++) {
- double speed = positions.get(i).getSpeed();
- speedSum += speed;
- if (speed > speedMax) {
- speedMax = speed;
- }
- }
-
- TripReport trip = new TripReport();
-
- long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime();
- long deviceId = startTrip.getDeviceId();
- trip.setDeviceId(deviceId);
- trip.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
-
- trip.setStartPositionId(startTrip.getId());
- trip.setStartLat(startTrip.getLatitude());
- trip.setStartLon(startTrip.getLongitude());
- trip.setStartTime(startTrip.getFixTime());
- trip.setStartAddress(startTrip.getAddress());
-
- trip.setEndPositionId(endTrip.getId());
- trip.setEndLat(endTrip.getLatitude());
- trip.setEndLon(endTrip.getLongitude());
- trip.setEndTime(endTrip.getFixTime());
- trip.setEndAddress(endTrip.getAddress());
-
- trip.setDistance(ReportUtils.calculateDistance(startTrip, endTrip, !ignoreOdometer));
- trip.setDuration(tripDuration);
- trip.setAverageSpeed(speedSum / (endIndex - startIndex));
- trip.setMaxSpeed(speedMax);
- trip.setSpentFuel(ReportUtils.calculateFuel(startTrip, endTrip));
-
- return trip;
- }
-
- protected static Collection<TripReport> detectTrips(
- double speedThreshold, double minimalTripDistance,
- long minimalTripDuration, long minimalParkingDuration, boolean greedyParking, boolean ignoreOdometer,
- Collection<Position> positionCollection) {
-
- Collection<TripReport> result = new ArrayList<>();
-
- ArrayList<Position> positions = new ArrayList<>(positionCollection);
- if (positions != null && !positions.isEmpty()) {
- int previousStartParkingIndex = 0;
- int startParkingIndex = -1;
- int previousEndParkingIndex = 0;
- int endParkingIndex = 0;
-
- boolean isMoving = false;
- boolean isLast = false;
- boolean skipped = false;
- boolean tripFiltered = false;
-
- for (int i = 0; i < positions.size(); i++) {
- isMoving = positions.get(i).getSpeed() > speedThreshold;
- isLast = i == positions.size() - 1;
-
- if ((isMoving || isLast) && startParkingIndex != -1) {
- if (!skipped || previousEndParkingIndex == 0) {
- previousEndParkingIndex = endParkingIndex;
- }
- endParkingIndex = i;
- }
- if (!isMoving && startParkingIndex == -1) {
- if (greedyParking) {
- long tripDuration = positions.get(i).getFixTime().getTime()
- - positions.get(endParkingIndex).getFixTime().getTime();
- double tripDistance = ReportUtils.calculateDistance(positions.get(endParkingIndex),
- positions.get(i), false);
- tripFiltered = tripDuration < minimalTripDuration && tripDistance < minimalTripDistance;
- if (tripFiltered) {
- startParkingIndex = previousStartParkingIndex;
- endParkingIndex = previousEndParkingIndex;
- tripFiltered = false;
- } else {
- previousStartParkingIndex = i;
- startParkingIndex = i;
- }
- } else {
- long tripDuration = positions.get(i).getFixTime().getTime()
- - positions.get(previousEndParkingIndex).getFixTime().getTime();
- double tripDistance = ReportUtils.calculateDistance(positions.get(previousEndParkingIndex),
- positions.get(i), false);
- tripFiltered = tripDuration < minimalTripDuration && tripDistance < minimalTripDistance;
- startParkingIndex = i;
- }
- }
- if (startParkingIndex != -1 && (endParkingIndex > startParkingIndex || isLast)) {
- long parkingDuration = positions.get(endParkingIndex).getFixTime().getTime()
- - positions.get(startParkingIndex).getFixTime().getTime();
- if ((parkingDuration >= minimalParkingDuration || isLast)
- && previousEndParkingIndex < startParkingIndex) {
- if (!tripFiltered) {
- result.add(calculateTrip(
- positions, previousEndParkingIndex, startParkingIndex, ignoreOdometer));
- }
- previousEndParkingIndex = endParkingIndex;
- skipped = false;
- } else {
- skipped = true;
- }
- startParkingIndex = -1;
- }
- }
- }
-
- return result;
- }
-
private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
- long minimalTripDuration = Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000;
- double minimalTripDistance = Context.getConfig().getLong("report.trip.minimalTripDistance", 500);
- long minimalParkingDuration = Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000;
- boolean greedyParking = Context.getConfig().getBoolean("report.trip.greedyParking");
+
+ TripsConfig tripsConfig = ReportUtils.initTripsConfig();
boolean ignoreOdometer = Context.getDeviceManager()
.lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
- return detectTrips(
- speedThreshold, minimalTripDistance, minimalTripDuration,
- minimalParkingDuration, greedyParking, ignoreOdometer,
- Context.getDataManager().getPositions(deviceId, from, to));
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig,
+ ignoreOdometer, speedThreshold,
+ Context.getDataManager().getPositions(deviceId, from, to), true);
+
+ return (Collection<TripReport>) result;
}
public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
diff --git a/src/org/traccar/reports/model/BaseReport.java b/src/org/traccar/reports/model/BaseReport.java
index 13c67cbb7..e1dec1407 100644
--- a/src/org/traccar/reports/model/BaseReport.java
+++ b/src/org/traccar/reports/model/BaseReport.java
@@ -74,4 +74,14 @@ public class BaseReport {
}
}
+ private String spentFuel;
+
+ public String getSpentFuel() {
+ return spentFuel;
+ }
+
+ public void setSpentFuel(String spentFuel) {
+ this.spentFuel = spentFuel;
+ }
+
}
diff --git a/src/org/traccar/reports/model/StopReport.java b/src/org/traccar/reports/model/StopReport.java
new file mode 100644
index 000000000..6b2e86299
--- /dev/null
+++ b/src/org/traccar/reports/model/StopReport.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.reports.model;
+
+import java.util.Date;
+
+public class StopReport extends BaseReport {
+
+ private long positionId;
+
+ public long getPositionId() {
+ return positionId;
+ }
+
+ public void setPositionId(long positionId) {
+ this.positionId = positionId;
+ }
+
+ private double latitude;
+
+ public double getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(double latitude) {
+ this.latitude = latitude;
+ }
+
+ private double longitude;
+
+ public double getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(double longitude) {
+ this.longitude = longitude;
+ }
+
+ private Date startTime;
+
+ public Date getStartTime() {
+ if (startTime != null) {
+ return new Date(startTime.getTime());
+ } else {
+ return null;
+ }
+ }
+
+ public void setStartTime(Date startTime) {
+ if (startTime != null) {
+ this.startTime = new Date(startTime.getTime());
+ } else {
+ this.startTime = null;
+ }
+ }
+
+ private Date endTime;
+
+ public Date getEndTime() {
+ if (endTime != null) {
+ return new Date(endTime.getTime());
+ } else {
+ return null;
+ }
+ }
+
+ public void setEndTime(Date endTime) {
+ if (endTime != null) {
+ this.endTime = new Date(endTime.getTime());
+ } else {
+ this.endTime = null;
+ }
+ }
+
+ private String address;
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ private long duration;
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+ private long engineHours; // milliseconds
+
+ public long getEngineHours() {
+ return engineHours;
+ }
+
+ public void setEngineHours(long engineHours) {
+ this.engineHours = engineHours;
+ }
+
+ public void addEngineHours(long engineHours) {
+ this.engineHours += engineHours;
+ }
+}
diff --git a/src/org/traccar/reports/model/SummaryReport.java b/src/org/traccar/reports/model/SummaryReport.java
index 85ba6bc54..6f9e9459f 100644
--- a/src/org/traccar/reports/model/SummaryReport.java
+++ b/src/org/traccar/reports/model/SummaryReport.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,15 +31,4 @@ public class SummaryReport extends BaseReport {
public void addEngineHours(long engineHours) {
this.engineHours += engineHours;
}
-
- private String spentFuel;
-
- public String getSpentFuel() {
- return spentFuel;
- }
-
- public void setSpentFuel(String spentFuel) {
- this.spentFuel = spentFuel;
- }
-
}
diff --git a/src/org/traccar/reports/model/TripReport.java b/src/org/traccar/reports/model/TripReport.java
index fce805ad6..efe556f79 100644
--- a/src/org/traccar/reports/model/TripReport.java
+++ b/src/org/traccar/reports/model/TripReport.java
@@ -145,15 +145,4 @@ public class TripReport extends BaseReport {
public void setDuration(long duration) {
this.duration = duration;
}
-
- private String spentFuel;
-
- public String getSpentFuel() {
- return spentFuel;
- }
-
- public void setSpentFuel(String spentFuel) {
- this.spentFuel = spentFuel;
- }
-
}
diff --git a/src/org/traccar/reports/model/TripsConfig.java b/src/org/traccar/reports/model/TripsConfig.java
new file mode 100644
index 000000000..55258a6cf
--- /dev/null
+++ b/src/org/traccar/reports/model/TripsConfig.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.reports.model;
+
+public class TripsConfig {
+
+ public TripsConfig() {
+ }
+
+ public TripsConfig(double minimalTripDistance, long minimalTripDuration,
+ long minimalParkingDuration, boolean greedyParking) {
+ this.minimalTripDistance = minimalTripDistance;
+ this.minimalTripDuration = minimalTripDuration;
+ this.minimalParkingDuration = minimalParkingDuration;
+ this.greedyParking = greedyParking;
+ }
+
+ private double minimalTripDistance;
+
+ public double getMinimalTripDistance() {
+ return minimalTripDistance;
+ }
+
+ public void setMinimalTripDistance(double minimalTripDistance) {
+ this.minimalTripDistance = minimalTripDistance;
+ }
+
+ private long minimalTripDuration;
+
+ public long getMinimalTripDuration() {
+ return minimalTripDuration;
+ }
+
+ public void setMinimalTripDuration(long minimalTripDuration) {
+ this.minimalTripDuration = minimalTripDuration;
+ }
+
+ private long minimalParkingDuration;
+
+ public long getMinimalParkingDuration() {
+ return minimalParkingDuration;
+ }
+
+ public void setMinimalParkingDuration(long minimalParkingDuration) {
+ this.minimalParkingDuration = minimalParkingDuration;
+ }
+
+ private boolean greedyParking;
+
+ public boolean getGreedyParking() {
+ return greedyParking;
+ }
+
+ public void setGreedyParking(boolean greedyParking) {
+ this.greedyParking = greedyParking;
+ }
+}
diff --git a/templates/export/stops.xlsx b/templates/export/stops.xlsx
new file mode 100644
index 000000000..d6bb4c3b9
--- /dev/null
+++ b/templates/export/stops.xlsx
Binary files differ
diff --git a/test/org/traccar/reports/TripsAndStopsTest.java b/test/org/traccar/reports/TripsAndStopsTest.java
new file mode 100644
index 000000000..aa2205c91
--- /dev/null
+++ b/test/org/traccar/reports/TripsAndStopsTest.java
@@ -0,0 +1,199 @@
+package org.traccar.reports;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.Position;
+import org.traccar.reports.model.BaseReport;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class TripsAndStopsTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private Position position(String time, double speed, double totalDistance) throws ParseException {
+
+ Position position = new Position();
+
+ if (time != null) {
+ position.setTime(date(time));
+ }
+ position.setValid(true);
+ position.setSpeed(speed);
+ position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
+
+ return position;
+ }
+
+ @Test
+ public void testDetectTripsSimple() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000),
+ position("2016-01-01 00:07:00.000", 0, 3000));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, false);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, true);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ TripReport itemTrip = (TripReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = (StopReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsOnly() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 1, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, false);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = (StopReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(300000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsWithTripCut() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 2, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, false);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = (StopReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getEndTime());
+ assertEquals(240000, itemStop.getDuration());
+
+ tripsConfig.setGreedyParking(true);
+
+ result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ itemStop = (StopReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(300000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsStartedFromTrip() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 2, 0),
+ position("2016-01-01 00:01:00.000", 1, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 0, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, false);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = (StopReport) result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(300000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsMoving() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 5, 0),
+ position("2016-01-01 00:01:00.000", 5, 0),
+ position("2016-01-01 00:02:00.000", 3, 0),
+ position("2016-01-01 00:03:00.000", 5, 0),
+ position("2016-01-01 00:04:00.000", 5, 0),
+ position("2016-01-01 00:05:00.000", 5, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, false);
+
+ Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig, false, 0.01, data, false);
+
+ assertNotNull(result);
+ assertTrue(result.isEmpty());
+
+ }
+
+}
diff --git a/test/org/traccar/reports/TripsTest.java b/test/org/traccar/reports/TripsTest.java
deleted file mode 100644
index dea101cf2..000000000
--- a/test/org/traccar/reports/TripsTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.traccar.reports;
-
-import org.junit.Test;
-import org.traccar.BaseTest;
-import org.traccar.model.Position;
-import org.traccar.reports.model.TripReport;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.TimeZone;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-
-public class TripsTest extends BaseTest {
-
- private Date date(String time) throws ParseException {
- DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- return dateFormat.parse(time);
- }
-
- private Position position(String time, double speed, double totalDistance) throws ParseException {
-
- Position position = new Position();
-
- if (time != null) {
- position.setTime(date(time));
- }
- position.setValid(true);
- position.setSpeed(speed);
- position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
-
- return position;
- }
-
- @Test
- public void testDetectTripsSimple() throws ParseException {
-
- Collection<Position> data = Arrays.asList(
- position("2016-01-01 00:00:00.000", 0, 0),
- position("2016-01-01 00:01:00.000", 0, 0),
- position("2016-01-01 00:02:00.000", 10, 0),
- position("2016-01-01 00:03:00.000", 10, 1000),
- position("2016-01-01 00:04:00.000", 10, 2000),
- position("2016-01-01 00:05:00.000", 0, 3000),
- position("2016-01-01 00:06:00.000", 0, 3000));
-
- Collection<TripReport> result = Trips.detectTrips(0.01, 500, 300000, 300000, false, false, data);
-
- assertNotNull(result);
- assertFalse(result.isEmpty());
-
- TripReport item = result.iterator().next();
-
- assertEquals(date("2016-01-01 00:02:00.000"), item.getStartTime());
- assertEquals(date("2016-01-01 00:05:00.000"), item.getEndTime());
- assertEquals(180000, item.getDuration());
- assertEquals(10, item.getAverageSpeed(), 0.01);
- assertEquals(10, item.getMaxSpeed(), 0.01);
- assertEquals(3000, item.getDistance(), 0.01);
-
- }
-
-}