com.hamdikavak.humanmobility.modeling.helpers.LocationTraceHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.hamdikavak.humanmobility.modeling.helpers.LocationTraceHelper.java

Source

/*
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;
    }
}