Java tutorial
/* MIT License Copyright (c) 2017 Hamdi Kavak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.hamdikavak.humanmobility.modeling.helpers; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.time.DateTime; import org.joda.time.Duration; import org.joda.time.Interval; import org.joda.time.LocalDateTime; import com.hamdikavak.humanmobility.modeling.spatial.DistanceWithUnit; import com.hamdikavak.humanmobility.modeling.spatial.ExtendedLocationTrace; import com.hamdikavak.humanmobility.modeling.spatial.LocationTrace; import com.hamdikavak.humanmobility.modeling.spatial.SpatialDistanceUnit; import com.hamdikavak.humanmobility.modeling.spatial.SpatialOperationHandler; public class LocationTraceHelper { private static final long defaultDurationInMinutes = 30L; // 30 minutes private static final long defaultDistanceInMeters = 70L; // 70 meters private static final Duration defaultDuration = new Duration(defaultDurationInMinutes * 60L * 1000L); private static final DistanceWithUnit defaultDistance = new DistanceWithUnit(defaultDistanceInMeters, SpatialDistanceUnit.Meter); private SpatialOperationHandler spatialOperation; public LocationTraceHelper() { spatialOperation = new SpatialOperationHandler(); } /** * This methods returns a unique location Id number list using given trace list. * @param traces trace list * @return unique ids in a list */ public List<Long> extractUniqueLocationIds(List<ExtendedLocationTrace> traces) { HashMap<Long, Boolean> idMap = new HashMap<Long, Boolean>(); for (ExtendedLocationTrace aTrace : traces) { // all location ids start from 1. if there is a lower number, it is likely that DBSCAN assigned noise to that visit if (aTrace.getLocationId() > 0) { idMap.put(aTrace.getLocationId(), true); } } return (new ArrayList<Long>(idMap.keySet())); } /** * This methods returns a unique location Id number list using given trace list. * @param traces trace list * @return unique ids in a list */ public HashMap<Long, Integer> extractUniqueLocationIdCounts(List<ExtendedLocationTrace> traces) { HashMap<Long, Integer> idMap = new HashMap<Long, Integer>(); for (ExtendedLocationTrace aTrace : traces) { // all location ids start from 1. if there is a lower number, it is likely that DBSCAN assigned noise to that visit if (aTrace.getLocationId() > 0) { int count = 0; if (idMap.containsKey(aTrace.getLocationId()) == true) { count = idMap.get(aTrace.getLocationId()); } count++; idMap.put(aTrace.getLocationId(), count); } } return idMap; } public List<LocationTrace> selectBetweenDates(List<LocationTrace> traces, LocalDateTime startDateInclusive, LocalDateTime endDateExclusive) { List<LocationTrace> newTraces = new ArrayList<LocationTrace>(); for (LocationTrace aTrace : traces) { if (aTrace.getLocalTime().toDateTime().getMillis() >= startDateInclusive.toDateTime().getMillis() && aTrace.getLocalTime().toDateTime().getMillis() < endDateExclusive.toDateTime().getMillis()) { newTraces.add(new LocationTrace(aTrace)); } } return newTraces; } /** * Selects traces that are in given start and end hours (24 hour based.) * @param traces * @param startHourInclusive * @param endHourInclusive * @return a list of cleaned traces. */ public List<ExtendedLocationTrace> selectBetweenHours(List<ExtendedLocationTrace> traces, int startHourInclusive, int endHourInclusive) { List<ExtendedLocationTrace> newTraces = new ArrayList<ExtendedLocationTrace>(); for (ExtendedLocationTrace aTrace : traces) { int hour = aTrace.getLocalTime().getHourOfDay(); if (startHourInclusive <= endHourInclusive && hour <= endHourInclusive && hour >= startHourInclusive) { newTraces.add(new ExtendedLocationTrace(aTrace)); } else if (startHourInclusive > endHourInclusive && (hour <= endHourInclusive || hour >= startHourInclusive)) { newTraces.add(new ExtendedLocationTrace(aTrace)); } } return newTraces; } /** * Cleans traces that shared within a sort period of time from the same place. * Assumes that traces are already assigned place ids. * <p> * Default duration {@value #defaultDurationInMinutes} minutes. * @param traces list of traces. location id of traces has to be set. * @return a list of cleaned traces. */ public List<ExtendedLocationTrace> cleanRepeatedTraces(List<ExtendedLocationTrace> traces) { return cleanRepeatedTraces(traces, defaultDuration); } /** * Cleans traces that shared within a sort period of time from the same place * @param traces list of traces. location id of traces has to be set. * @param duration threshold length to identify repeated consequent traces * @return a list of cleaned traces. */ public List<ExtendedLocationTrace> cleanRepeatedTraces(List<ExtendedLocationTrace> traces, Duration duration) { // check whether we have enough number of traces if (traces == null || traces.size() < 2) { return traces; } // at this point, we have at least two traces Iterator<ExtendedLocationTrace> traceIterator = traces.iterator(); ExtendedLocationTrace firstTrace = traceIterator.next(); // auto-assign first trace ExtendedLocationTrace secondTrace = null; // to be assigned while (traceIterator.hasNext()) { // get the next item secondTrace = traceIterator.next(); DateTime firstDate = firstTrace.getUTCTime(); DateTime secondDate = secondTrace.getUTCTime(); // calculate time interval between the two Interval interval = new Interval(firstDate, secondDate); // the following if statement checks whether both traces report the same place // and their inter-event duration is shorter than our threshold duration if (firstTrace.getLocationId() == secondTrace.getLocationId() && interval.toDuration().isShorterThan(duration)) { // this means the user reported his/her location shortly after one another. // we delete this record. traceIterator.remove(); } else { // this means, didn't report from the same location within a certain time after the first report. firstTrace = secondTrace; } } return traces; } /** * Cleans traces that are shared within a sort period of time from a nearby * location. This method is different from * {@code LocationTraceHelper.cleanRepeatedTraces} method in a way that this * doesn't require pre-assigned location ids to traces. Use other overload * methods to set specific distance and duration values. * <p> * Default duration {@value #defaultDurationInMinutes} minutes. Default * distance {@value #defaultDistanceInMeters} meters. * * @param traces list of traces. * @return a list of cleaned traces. */ public List<ExtendedLocationTrace> cleanRepeatedTracesByDistance(List<ExtendedLocationTrace> traces) { return cleanRepeatedTracesByDistance(traces, defaultDistance, defaultDuration); } public List<ExtendedLocationTrace> cleanRepeatedTracesByDistance(List<ExtendedLocationTrace> traces, DistanceWithUnit distance) { return cleanRepeatedTracesByDistance(traces, distance, defaultDuration); } public List<ExtendedLocationTrace> cleanRepeatedTracesByDistance(List<ExtendedLocationTrace> traces, Duration duration) { return cleanRepeatedTracesByDistance(traces, defaultDistance, duration); } /** * * Cleans traces that are shared within a given sort period of time from a * given nearby location distance. This method is different from * {@code LocationTraceHelper.cleanRepeatedTraces} method in a way that this * doesn't require pre-assigned location ids to traces. * * @param traces list of traces. * @param distance threshold spatial distance to identify repeated consequent traces * @param duration threshold length to identify repeated consequent traces * @return a list of cleaned traces. */ public List<ExtendedLocationTrace> cleanRepeatedTracesByDistance(List<ExtendedLocationTrace> traces, DistanceWithUnit distance, Duration duration) { // check whether we have enough number of traces if (traces == null || traces.size() < 2) { return traces; } // at this point, we have at least two traces Iterator<ExtendedLocationTrace> traceIterator = traces.iterator(); ExtendedLocationTrace firstTrace = traceIterator.next(); // auto-assign first trace ExtendedLocationTrace secondTrace = null; // to be assigned while (traceIterator.hasNext()) { // get the next item secondTrace = traceIterator.next(); DateTime firstDate = firstTrace.getUTCTime(); DateTime secondDate = secondTrace.getUTCTime(); // calculate time interval between the two Interval interval = new Interval(firstDate, secondDate); // the following if statement checks whether both traces report within a short distance // and their inter-event duration is shorter than our threshold duration double calculatedDistance = spatialOperation.calculateDistance(firstTrace.getCoordinate(), secondTrace.getCoordinate(), SpatialDistanceUnit.Meter); DistanceWithUnit calculatedDistanceObject = new DistanceWithUnit(calculatedDistance, SpatialDistanceUnit.Meter); if (calculatedDistanceObject.isShortherThan(distance) && interval.toDuration().isShorterThan(duration)) { // this means the user reported his/her location shortly after one another. // we delete this record. traceIterator.remove(); } else { // this means, didn't report from the same location within a certain time after the first report. firstTrace = secondTrace; } } return traces; } /** * Selects traces that contain given keyword * @param traces * @param keyword * @return a list of selected location traces that contain given keyword. */ public List<ExtendedLocationTrace> selectContainingKeyword(List<ExtendedLocationTrace> traces, String keyword) { List<ExtendedLocationTrace> newTraces = new ArrayList<ExtendedLocationTrace>(); Pattern regex = Pattern.compile("(?i)\\b" + keyword + "\\b"); Matcher regexMatcher; for (ExtendedLocationTrace aTrace : traces) { regexMatcher = regex.matcher(aTrace.getNote()); if (regexMatcher.find() == true) { newTraces.add(new ExtendedLocationTrace(aTrace)); } } return newTraces; } /** * Selects traces that has given location id * @param traces * @param keyword * @return a list of selected location traces that contain given keyword. */ public List<ExtendedLocationTrace> selectByLocationId(List<ExtendedLocationTrace> traces, long locationId) { List<ExtendedLocationTrace> newTraces = new ArrayList<ExtendedLocationTrace>(); for (ExtendedLocationTrace aTrace : traces) { if (aTrace.getLocationId() == locationId) { newTraces.add(new ExtendedLocationTrace(aTrace)); } } return newTraces; } }