|
@@ -4,11 +4,20 @@ import java.sql.ResultSet;
|
|
|
import java.sql.ResultSetMetaData;
|
|
import java.sql.ResultSetMetaData;
|
|
|
import java.sql.SQLException;
|
|
import java.sql.SQLException;
|
|
|
import java.sql.Types;
|
|
import java.sql.Types;
|
|
|
-import java.util.List;
|
|
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.time.LocalTime;
|
|
|
|
|
+import java.time.OffsetDateTime;
|
|
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.regex.Matcher;
|
|
|
|
|
+import java.util.regex.Pattern;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
import cz.hsrs.db.model.Unit;
|
|
import cz.hsrs.db.model.Unit;
|
|
|
import cz.hsrs.db.pool.SQLExecutor;
|
|
import cz.hsrs.db.pool.SQLExecutor;
|
|
|
|
|
|
|
|
|
|
+import static java.util.stream.Collectors.toList;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
*
|
|
*
|
|
|
* @author mkepka
|
|
* @author mkepka
|
|
@@ -124,7 +133,101 @@ public class ExportUtil {
|
|
|
}
|
|
}
|
|
|
return CSV;
|
|
return CSV;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public static String getObservationsBySensorByGroup(long sensorId, int groupId, LocalDateTime fromTime, LocalDateTime toTime, boolean nullable) throws SQLException {
|
|
|
|
|
+ List<String> unitIds = UnitUtil.getUnitsBySensorIdByGroup(sensorId, groupId).stream().map(u -> String.valueOf(u.getUnitId())).collect(toList());
|
|
|
|
|
+ String unitIdsStr = String.join(",", unitIds);
|
|
|
|
|
+ return getObservationsBySensorByUnits(sensorId, unitIdsStr, groupId, fromTime, toTime, nullable);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String getObservationsBySensorByUnits(long sensorId, String unitIds, int groupId, LocalDateTime fromTime, LocalDateTime toTime, boolean nullable) throws SQLException {
|
|
|
|
|
+ // long sensorId, int groupId, String fromTime, String toTime, boolean nullable
|
|
|
|
|
+ if (!Pattern.compile("^[\\d,\\s]+$").matcher(unitIds).find()) {
|
|
|
|
|
+ throw new SQLException("unit_id does not contain only numbers.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String sqlPositionQuery = String.format("SELECT u_p.unit_id, ST_X(u_p.the_geom) AS lon, ST_Y(u_p.the_geom) AS lat FROM units_positions AS u_p\n" +
|
|
|
|
|
+ "JOIN units AS u ON u.unit_id = u_p.unit_id\n" +
|
|
|
|
|
+ "WHERE u_p.unit_id IN (%s) AND u.is_mobile = FALSE;", unitIds);
|
|
|
|
|
+
|
|
|
|
|
+ ResultSet unitRes = SQLExecutor.getInstance().executeQuery(sqlPositionQuery);
|
|
|
|
|
+ Map<Long, Location> unitToPosition = parseUnitPosition(unitRes);
|
|
|
|
|
+
|
|
|
|
|
+ String sqlObsQuery = String.format("SELECT unit_id, time_stamp, observed_value FROM observations\n" +
|
|
|
|
|
+ "WHERE unit_id IN (%s) AND sensor_id = %d AND time_stamp >= '%s' AND time_stamp <= '%s' ORDER BY time_stamp;",
|
|
|
|
|
+ unitIds, sensorId, fromTime, toTime);
|
|
|
|
|
+ ResultSet obsRes = SQLExecutor.getInstance().executeQuery(sqlObsQuery);
|
|
|
|
|
+ Map<Long, Map<LocalDateTime, List<Double>>> unitToObs = parseUnitObservation(obsRes);
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder csv = new StringBuilder("unit_id;longitude;latitude");
|
|
|
|
|
+ LocalDateTime tempTime = LocalDateTime.of(fromTime.toLocalDate(), LocalTime.of(fromTime.getHour(), 0));
|
|
|
|
|
+ List<LocalDateTime> timestampColumns = new LinkedList<>();
|
|
|
|
|
+ final DateTimeFormatter timestampPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ while(tempTime.isBefore(toTime)) {
|
|
|
|
|
+ timestampColumns.add(tempTime);
|
|
|
|
|
+ csv.append(";").append(tempTime.format(timestampPattern));
|
|
|
|
|
+ tempTime = tempTime.plusHours(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ csv.append("\n");
|
|
|
|
|
+
|
|
|
|
|
+ for (Map.Entry<Long, Map<LocalDateTime, List<Double>>> unitToEntry : unitToObs.entrySet()) {
|
|
|
|
|
+ long unitId = unitToEntry.getKey();
|
|
|
|
|
+ Map<LocalDateTime, List<Double>> obsAtTimestamp = unitToEntry.getValue();
|
|
|
|
|
+ Location unitLocation = unitToPosition.get(unitId);
|
|
|
|
|
+ csv.append(unitId).append(";").append(unitLocation.lon).append(";").append(unitLocation.lat);
|
|
|
|
|
+ for (LocalDateTime timestampColumn : timestampColumns) {
|
|
|
|
|
+ double val = obsAtTimestamp.getOrDefault(timestampColumn, Collections.emptyList()).stream().mapToDouble(d -> d).average().orElse(Double.NaN);
|
|
|
|
|
+ csv.append(";").append(Double.isNaN(val) ? "" : val);
|
|
|
|
|
+ }
|
|
|
|
|
+ csv.append("\n");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return csv.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Map<Long, Map<LocalDateTime, List<Double>>> parseUnitObservation(ResultSet res) throws SQLException {
|
|
|
|
|
+ if (res == null) {
|
|
|
|
|
+ throw new SQLException("Can not load observations.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX");
|
|
|
|
|
+ Map<Long, Map<LocalDateTime, List<Double>>> unitToObs = new HashMap<>();
|
|
|
|
|
+ while(res.next()) {
|
|
|
|
|
+ long unitId = res.getLong("unit_id");
|
|
|
|
|
+ String timestampStr = res.getString("time_stamp");
|
|
|
|
|
+ LocalDateTime timestamp = LocalDateTime.parse(timestampStr, formatter);
|
|
|
|
|
+ LocalDateTime timestampAtHour = LocalDateTime.of(timestamp.toLocalDate(), LocalTime.of(timestamp.getHour(), 0));
|
|
|
|
|
+ double value = res.getDouble("observed_value");
|
|
|
|
|
+ unitToObs.computeIfAbsent(unitId, k -> new HashMap<>())
|
|
|
|
|
+ .computeIfAbsent(timestampAtHour, k -> new LinkedList<>())
|
|
|
|
|
+ .add(value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return unitToObs;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static class Location {
|
|
|
|
|
+ String lon, lat;
|
|
|
|
|
+ public Location(double lon, double lat) {
|
|
|
|
|
+ this.lon = lon == 0 ? "" : String.valueOf(lon);
|
|
|
|
|
+ this.lat = lat == 0 ? "" : String.valueOf(lat);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Map<Long, Location> parseUnitPosition(ResultSet res) throws SQLException {
|
|
|
|
|
+ if (res == null) {
|
|
|
|
|
+ throw new SQLException("Can not load unit's locations.");
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, Location> unitToPosition = new HashMap<>();
|
|
|
|
|
+ while(res.next()) {
|
|
|
|
|
+ long unitId = res.getLong("unit_id");
|
|
|
|
|
+ double lon = res.getDouble("lon");
|
|
|
|
|
+ double lat = res.getDouble("lat");
|
|
|
|
|
+ unitToPosition.put(unitId, new Location(lon, lat));
|
|
|
|
|
+ }
|
|
|
|
|
+ return unitToPosition;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
*
|
|
*
|
|
|
* @param res
|
|
* @param res
|