Java tutorial
/** * Copyright (c) 2011 Metropolitan Transportation Authority * * 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.onebusaway.nyc.vehicle_tracking.impl; import org.onebusaway.csv_entities.CsvEntityReader; import org.onebusaway.geospatial.model.CoordinateBounds; import org.onebusaway.geospatial.model.CoordinatePoint; import org.onebusaway.geospatial.services.SphericalGeometryLibrary; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.services.calendar.CalendarService; import org.onebusaway.nyc.transit_data_federation.bundle.tasks.stif.model.RunTripEntry; import org.onebusaway.nyc.transit_data_federation.services.nyc.DestinationSignCodeService; import org.onebusaway.nyc.transit_data_federation.services.nyc.RunService; import org.onebusaway.nyc.vehicle_tracking.impl.inference.ObservationCache; import org.onebusaway.nyc.vehicle_tracking.impl.simulator.SimulatorTask; import org.onebusaway.nyc.vehicle_tracking.model.NycRawLocationRecord; import org.onebusaway.nyc.vehicle_tracking.model.NycTestInferredLocationRecord; import org.onebusaway.nyc.vehicle_tracking.model.csv.TabTokenizerStrategy; import org.onebusaway.nyc.vehicle_tracking.model.simulator.VehicleLocationDetails; import org.onebusaway.nyc.vehicle_tracking.model.simulator.VehicleLocationSimulationSummary; import org.onebusaway.nyc.vehicle_tracking.services.VehicleLocationSimulationService; import org.onebusaway.nyc.vehicle_tracking.services.inference.VehicleLocationInferenceService; import org.onebusaway.realtime.api.EVehiclePhase; import org.onebusaway.realtime.api.VehicleLocationListener; import org.onebusaway.transit_data_federation.services.AgencyAndIdLibrary; import org.onebusaway.transit_data_federation.services.blocks.BlockCalendarService; import org.onebusaway.transit_data_federation.services.blocks.BlockInstance; import org.onebusaway.transit_data_federation.services.blocks.ScheduledBlockLocation; import org.onebusaway.transit_data_federation.services.blocks.ScheduledBlockLocationService; import org.onebusaway.transit_data_federation.services.transit_graph.BlockConfigurationEntry; import org.onebusaway.transit_data_federation.services.transit_graph.BlockEntry; import org.onebusaway.transit_data_federation.services.transit_graph.BlockStopTimeEntry; import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry; import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry; import org.onebusaway.utility.EOutOfRangeStrategy; import org.onebusaway.utility.InterpolationLibrary; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import umontreal.iro.lecuyer.probdist.DiscreteDistribution; import umontreal.iro.lecuyer.randvar.UniformGen; import umontreal.iro.lecuyer.randvarmulti.DirichletGen; import umontreal.iro.lecuyer.rng.MRG32k3a; import umontreal.iro.lecuyer.rng.RandomStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class VehicleLocationSimulationServiceImpl implements VehicleLocationSimulationService { private final Logger _log = LoggerFactory.getLogger(VehicleLocationSimulationServiceImpl.class); private static final String ARG_VEHICLE_ID = "vehicle_id"; private static final String ARG_SHIFT_START_TIME = "shift_start_time"; private static final String ARG_LOCATION_SIGMA = "location_sigma"; private static final String ARG_SCHEDULE_DEVIATIONS = "schedule_deviations"; private static final String ARG_INCLUDE_START = "include_start"; private int _numberOfThreads = 20; private ExecutorService _executor; private VehicleLocationInferenceService _vehicleLocationInferenceService; private ScheduledBlockLocationService _scheduledBlockLocationService; private RunService _runService; private DestinationSignCodeService _destinationSignCodeService; private final Map<Integer, SimulatorTask> _tasks = new ConcurrentHashMap<Integer, SimulatorTask>(); private final ConcurrentMap<Object, List<SimulatorTask>> _tasksByTag = new ConcurrentHashMap<Object, List<SimulatorTask>>(); private final AtomicInteger _taskIndex = new AtomicInteger(); private BlockCalendarService _blockCalendarService; private CalendarService _calendarService; private final RandomStream streamArr = new MRG32k3a(); private ObservationCache _observationCache; @Autowired private VehicleLocationListener _vehicleLocationListener; // TODO consistent? private final UniformGen ung = new UniformGen(streamArr); @Autowired public void setObservationCache(ObservationCache observationCache) { _observationCache = observationCache; } @Autowired public void setCalendarService(CalendarService calendarService) { _calendarService = calendarService; } @Autowired public void setRunService(RunService runService) { _runService = runService; } @Autowired public void setVehicleLocationService(VehicleLocationInferenceService vehicleLocationService) { _vehicleLocationInferenceService = vehicleLocationService; } @Autowired public void setScheduledBlockLocationService(ScheduledBlockLocationService scheduledBlockLocationService) { _scheduledBlockLocationService = scheduledBlockLocationService; } @Autowired public void setDestinationSignCodeService(DestinationSignCodeService destinationSignCodeService) { _destinationSignCodeService = destinationSignCodeService; } @Autowired public void setBlockCalendarService(BlockCalendarService blockCalendarService) { _blockCalendarService = blockCalendarService; } public void setNumberOfThread(int numberOfThreads) { _numberOfThreads = numberOfThreads; } @PostConstruct public void start() { _executor = Executors.newFixedThreadPool(_numberOfThreads); } @PreDestroy public void stop() { _executor.shutdownNow(); } @Override public int simulateLocationsFromTrace(String filename, String traceType, InputStream traceInputStream, boolean runInRealtime, boolean pauseOnStart, boolean shiftStartTime, int minimumRecordInterval, boolean bypassInference, boolean fillActualProperties, boolean loop, int historySize) throws IOException { final SimulatorTask task = new SimulatorTask(); task.setFilename(filename); task.setPauseOnStart(pauseOnStart); task.setRunInRealtime(runInRealtime); task.setShiftStartTime(shiftStartTime); task.setMinimumRecordInterval(minimumRecordInterval); task.setBypassInference(bypassInference); task.setFillActualProperties(fillActualProperties); task.setLoop(loop); task.setMaxParticleHistorySize(historySize); final CsvEntityReader reader = new CsvEntityReader(); reader.addEntityHandler(task); if (traceType.equals("NycRawLocationRecord")) { reader.setTokenizerStrategy(new TabTokenizerStrategy()); reader.readEntities(NycRawLocationRecord.class, traceInputStream); } else if (traceType.equals("NycTestInferredLocationRecord")) { reader.readEntities(NycTestInferredLocationRecord.class, traceInputStream); } traceInputStream.close(); return addTask(task); } @Override public List<VehicleLocationSimulationSummary> getSimulations() { final List<VehicleLocationSimulationSummary> summaries = new ArrayList<VehicleLocationSimulationSummary>(); for (final SimulatorTask task : _tasks.values()) summaries.add(task.getSummary()); return summaries; } @Override public VehicleLocationSimulationSummary getSimulation(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getSummary(); return null; } @Override public VehicleLocationDetails getSimulationDetails(int taskId, int recordNumber) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getDetails(recordNumber); return null; } @Override public VehicleLocationDetails getParticleDetails(int taskId, int particleId, int recordIndex) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getParticleDetails(particleId, recordIndex); return null; } @Override public VehicleLocationDetails getTransitionParticleDetails(int taskId, int particleId, int transParticleNum, int recordIndex) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getTransitionParticleDetails(particleId, transParticleNum, recordIndex); return null; } @Override public List<NycTestInferredLocationRecord> getResultRecords(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getResults(); return null; } @Override public List<NycTestInferredLocationRecord> getSimulationRecords(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) return task.getRecords(); return Collections.emptyList(); } @Override public void toggleSimulation(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) task.toggle(); } @Override public void stepSimulation(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) task.step(); } @Override public void stepSimulation(int taskId, int recordIndex) { final SimulatorTask task = _tasks.get(taskId); if (task != null) task.step(recordIndex); } @Override public void restartSimulation(int taskId) { final SimulatorTask task = _tasks.get(taskId); if (task != null) { _observationCache.purge(task.getVehicleId()); task.restart(); if (task.isComplete()) _executor.execute(task); } } @Override public void cancelSimulation(int taskId) { final SimulatorTask task = _tasks.remove(taskId); if (task != null) { final Future<?> future = task.getFuture(); future.cancel(true); task.resetAllVehiclesAppearingInRecordData(); for (final Object tag : task.getTags()) { final List<SimulatorTask> list = getTasksForTag(tag, false); if (list != null) { synchronized (list) { list.remove(task); } } } } } @Override public void cancelAllSimulations() { final List<Integer> taskIds = new ArrayList<Integer>(_tasks.keySet()); for (final int taskId : taskIds) { SimulatorTask task = _tasks.get(taskId); _vehicleLocationListener.resetVehicleLocation(task.getVehicleId()); cancelSimulation(taskId); } } @Override public List<VehicleLocationSimulationSummary> getSimulationsForBlockInstance(AgencyAndId blockId, long serviceDate) { final String tag = generateBlockServiceDateTag(blockId, serviceDate); final List<SimulatorTask> tasks = getTasksForTag(tag, false); final List<VehicleLocationSimulationSummary> summaries = new ArrayList<VehicleLocationSimulationSummary>(); if (tasks != null) { for (final SimulatorTask task : tasks) summaries.add(task.getSummary()); } return summaries; } class RunTripComparator implements Comparator<RunTripEntry> { long _sd; BlockCalendarService _bcs; public RunTripComparator(long sd, BlockCalendarService bcs) { _sd = sd; _bcs = bcs; } @Override public int compare(RunTripEntry rte1, RunTripEntry rte2) { final TripEntry t1 = rte1.getTripEntry(); final TripEntry t2 = rte2.getTripEntry(); long sd1 = _bcs.getBlockInstance(t1.getBlock().getId(), _sd).getServiceDate(); long sd2 = _bcs.getBlockInstance(t2.getBlock().getId(), _sd).getServiceDate(); sd1 += rte1.getStartTime() * 1000; sd2 += rte2.getStartTime() * 1000; return sd1 < sd2 ? -1 : 1; } } public void generateRunSim(Random random, SimulatorTask task, RunTripEntry runTrip, long serviceDate, int scheduleTime, int shiftStartTime, boolean reportsOperatorId, boolean reportsRunId, boolean allowRunTransitions, SortedMap<Double, Integer> scheduleDeviations, double locationSigma, AgencyAndId vehicleId) { /* * here we format the runId to have a run-route that looks similar to what * an operator would enter. */ final String runNumber = runTrip.getRunNumber(); String runRoute = runTrip.getRunRoute(); if (StringUtils.equals(runRoute, "MISC")) { // runRoute = "0" + random.nextInt(9) + random.nextInt(9); runRoute = "000"; } else if (runRoute.length() >= 5) { final String firstPart = runRoute.substring(1, 3); final String lastPart = runRoute.substring(3); runRoute = "0" + (random.nextBoolean() ? firstPart : lastPart); } else { final String firstPart = runRoute.substring(1, 3); runRoute = "0" + firstPart; } final String reportedRunId = RunTripEntry.createId(runRoute, runNumber); if (reportsRunId) _log.info("using reported runId=" + reportedRunId); String lastBlockId = null; final List<RunTripEntry> rtes = new ArrayList<RunTripEntry>(); for (final RunTripEntry rte : _runService.getRunTripEntriesForRun(runTrip.getRunId())) { if (_calendarService.isLocalizedServiceIdActiveOnDate(rte.getTripEntry().getServiceId(), new Date(serviceDate))) rtes.add(rte); } if (rtes.isEmpty()) { _log.error("no active runTrips for service date=" + new Date(serviceDate)); } // TODO necessary? Collections.sort(rtes, new RunTripComparator(serviceDate, _blockCalendarService)); // String agencyId = runTrip.getTripEntry().getId().getAgencyId(); CoordinatePoint lastLocation = null; final RunTripEntry lastRunTrip = rtes.get(rtes.size() - 1); final int firstTime = runTrip.getStartTime(); final int lastTime = lastRunTrip.getStopTime(); int runningLastTime = runTrip.getStopTime(); int currRunIdx = rtes.indexOf(runTrip); // We could go by run trip entry sequence or perturbed schedule-time // evolution. The latter is chosen, for now. while (scheduleTime <= lastTime) { final NycTestInferredLocationRecord record = new NycTestInferredLocationRecord(); final long unperturbedTimestamp = serviceDate + (scheduleTime + shiftStartTime) * 1000; if (scheduleTime >= runningLastTime) { if (currRunIdx == rtes.size() - 1) break; currRunIdx += 1; runTrip = rtes.get(currRunIdx); // runTrip = _runService.getNextEntry(runTrip); runningLastTime = runTrip.getStopTime(); // FIXME saw weird run setups like this. are these in error? if (scheduleTime >= runningLastTime) { _log.error("runs are ordered oddly:" + rtes.get(currRunIdx - 1) + " -> " + runTrip); break; } } /* * this could mean that the run has ended. We could reassign the driver? * for now we'll terminate the simulation */ if (runTrip == null) break; final TripEntry trip = runTrip.getTripEntry(); final AgencyAndId tripId = trip.getId(); record.setActualTripId(AgencyAndIdLibrary.convertToString(tripId)); // TODO dsc changes for new block/run? String dsc = _destinationSignCodeService.getDestinationSignCodeForTripId(tripId); if (StringUtils.isEmpty(dsc)) dsc = "0"; // FIXME straighten these out... if (scheduleTime < runTrip.getStartTime()) { record.setActualPhase(EVehiclePhase.DEADHEAD_BEFORE.toString()); dsc = "0"; } else { record.setActualPhase(EVehiclePhase.IN_PROGRESS.toString()); } int scheduleDeviation = 0; if (!scheduleDeviations.isEmpty()) { final double ratio = (scheduleTime - firstTime) / ((double) (lastTime - firstTime)); scheduleDeviation = (int) InterpolationLibrary.interpolate(scheduleDeviations, ratio, EOutOfRangeStrategy.LAST_VALUE); } final long perterbedTimestamp = unperturbedTimestamp + scheduleDeviation * 1000; /* * sample runTrips active 30 minutes from now. TODO make the time range * less arbitrary? */ if (allowRunTransitions) { final RunTripEntry newRun = sampleNearbyRunTrips(runTrip, unperturbedTimestamp + 30 * 60 * 1000); if (newRun != null) { // FIXME can we get to this trip in 30, or whatever minutes? // FIXME what geometry do we follow to get there? /* * we need to simulate the dsc/headsign: could set it to 0 the new * trip the current trip ... TODO should this be a user option? */ record.setActualPhase(EVehiclePhase.DEADHEAD_BEFORE.toString()); dsc = "0"; runTrip = newRun; /* * similarly, do we reset the reportedRunId? TODO also a user option? */ // reportedRunId = runTrip.getRun(); } } // TODO when are there multiples and which do we choose when there are? final ScheduledBlockLocation blockLocation = _runService.getSchedBlockLocForRunTripEntryAndTime(runTrip, unperturbedTimestamp); if (blockLocation == null) break; final BlockEntry blockEntry = blockLocation.getActiveTrip().getBlockConfiguration().getBlock(); _log.debug("sim blockLocation: " + blockLocation.toString()); CoordinatePoint location = blockLocation.getLocation(); record.setActualRunId(runTrip.getRunId()); final String currentBlockId = AgencyAndIdLibrary.convertToString(blockEntry.getId()); // if (_log.isDebugEnabled()) if (lastBlockId != null && !StringUtils.equals(currentBlockId, lastBlockId)) { _log.info("changed blocks: " + lastBlockId + " -> " + currentBlockId); } record.setActualBlockId(currentBlockId); lastBlockId = currentBlockId; record.setActualDistanceAlongBlock(blockLocation.getDistanceAlongBlock()); /* * during block changes we get a weird null location. this is a * "work-around"... */ if (location == null) { location = lastLocation; } else { lastLocation = location; } final CoordinatePoint p = applyLocationNoise(location.getLat(), location.getLon(), locationSigma, random); record.setDsc(dsc); record.setLat(p.getLat()); record.setLon(p.getLon()); record.setTimestamp(perterbedTimestamp); record.setVehicleId(vehicleId); // TODO options for whether these are reported or not? if (reportsOperatorId) record.setOperatorId("0000"); if (reportsRunId) { record.setReportedRunId(reportedRunId); } record.setActualServiceDate(serviceDate); final int actualScheduleTime = blockLocation.getScheduledTime(); record.setActualScheduleTime(actualScheduleTime); record.setActualDsc(dsc); record.setActualBlockLat(location.getLat()); record.setActualBlockLon(location.getLon()); // TODO setActualStatus? task.addRecord(record); scheduleTime += 30 + random.nextGaussian() * 2; } } /** * This method samples runTrips that are active at the given time * * @param currentRunTrip * @param timestamp * @return */ private RunTripEntry sampleNearbyRunTrips(RunTripEntry currentRunTrip, long timestamp) { final List<RunTripEntry> activeRunTrips = _runService.getActiveRunTripEntriesForAgencyAndTime( currentRunTrip.getTripEntry().getId().getAgencyId(), timestamp); int bsize = activeRunTrips.size(); if (bsize < 1) { _log.warn("sampler: not enough runs to sample"); return null; } int currentIdx = activeRunTrips.lastIndexOf(currentRunTrip); if (currentIdx < 0) { activeRunTrips.add(currentRunTrip); currentIdx = activeRunTrips.lastIndexOf(currentRunTrip); bsize += 1; } final double[] probs = new double[bsize]; final double[] obs = new double[bsize]; for (int i = 0; i < bsize; ++i) { if (i == currentIdx) { // TODO Make this configurable, and explain probs[i] = 100 * bsize; } else { probs[i] = 1; } obs[i] = i; _log.debug(i + ": " + activeRunTrips.get(i)); } final DirichletGen rdg = new DirichletGen(streamArr, probs); rdg.nextPoint(probs); final DiscreteDistribution emd = new DiscreteDistribution(obs, probs, bsize); final double u = ung.nextDouble(); final int newIdx = (int) emd.inverseF(u); if (newIdx != currentIdx) _log.info(probs[newIdx] + ": sampled new idx=" + newIdx); return newIdx != currentIdx ? activeRunTrips.get(newIdx) : null; } /* * We make the distinction between whether a block or a run is driving this * simulation. However, we always initialize the simulation by a run that is * active for the "initialization" block, and thus trip, we were passed. * Really, what we have is block-inspired run simulations... */ @Override public int addSimulationForBlockInstance(AgencyAndId blockId, long serviceDate, long actualTime, boolean bypassInference, boolean isRunBased, boolean realtime, boolean fillActual, boolean reportsOperatorId, boolean reportsRunId, boolean allowRunTransitions, Properties properties) { final Random random = new Random(); final SimulatorTask task = new SimulatorTask(); final String tag = generateBlockServiceDateTag(blockId, serviceDate); task.addTag(tag); task.setPauseOnStart(false); task.setRunInRealtime(realtime); task.setBypassInference(bypassInference); task.setFillActualProperties(fillActual); final BlockInstance blockInstance = _blockCalendarService.getBlockInstance(blockId, serviceDate); final BlockConfigurationEntry block = blockInstance.getBlock(); int scheduleTime = (int) ((actualTime - serviceDate) / 1000); final int shiftStartTime = getShiftStartTimeProperty(properties); scheduleTime -= shiftStartTime; final AgencyAndId vehicleId = getVehicleIdProperty(random, properties, blockId.getAgencyId()); final double locationSigma = getLocationSigma(properties); final SortedMap<Double, Integer> scheduleDeviations = getScheduleDeviations(properties); if (isRunBased) { final RunTripEntry runTrip = _runService.getActiveRunTripEntryForBlockInstance(blockInstance, scheduleTime); if (runTrip == null) { _log.warn("no runTrip for blockInstance=" + blockInstance + " and scheduleTime=" + scheduleTime); return -1; } _log.info("Using runTrip=" + runTrip.toString()); // TODO pass these along to run-simulation method // List<Double> transitionParams = // getRunTransitionParams(properties); // TODO create "config" object to hold this mess? generateRunSim(random, task, runTrip, serviceDate, scheduleTime, shiftStartTime, reportsOperatorId, reportsRunId, allowRunTransitions, scheduleDeviations, locationSigma, vehicleId); return addTask(task); } final List<BlockStopTimeEntry> stopTimes = block.getStopTimes(); final BlockStopTimeEntry firstStopTime = stopTimes.get(0); final BlockStopTimeEntry lastStopTime = stopTimes.get(stopTimes.size() - 1); final int firstTime = firstStopTime.getStopTime().getArrivalTime(); final int lastTime = lastStopTime.getStopTime().getDepartureTime(); if (isIncludeStartTime(properties)) scheduleTime = firstTime; scheduleTime = Math.max(firstTime, scheduleTime); while (scheduleTime <= lastTime) { final NycTestInferredLocationRecord record = new NycTestInferredLocationRecord(); final ScheduledBlockLocation blockLocation = _scheduledBlockLocationService .getScheduledBlockLocationFromScheduledTime(block, scheduleTime); /** * Not in service? */ if (blockLocation == null) return -1; final BlockTripEntry trip = blockLocation.getActiveTrip(); final AgencyAndId tripId = trip.getTrip().getId(); final CoordinatePoint location = blockLocation.getLocation(); record.setActualBlockId(AgencyAndIdLibrary.convertToString(blockId)); record.setActualDistanceAlongBlock(blockLocation.getDistanceAlongBlock()); final int actualScheduleTime = blockLocation.getScheduledTime(); String dsc = _destinationSignCodeService.getDestinationSignCodeForTripId(tripId); if (dsc == null) dsc = "0"; int scheduleDeviation = 0; if (!scheduleDeviations.isEmpty()) { final double ratio = (scheduleTime - firstTime) / ((double) (lastTime - firstTime)); scheduleDeviation = (int) InterpolationLibrary.interpolate(scheduleDeviations, ratio, EOutOfRangeStrategy.LAST_VALUE); } final long timestamp = serviceDate + (scheduleTime + shiftStartTime + scheduleDeviation) * 1000; final CoordinatePoint p = applyLocationNoise(location.getLat(), location.getLon(), locationSigma, random); record.setDsc(dsc); record.setLat(p.getLat()); record.setLon(p.getLon()); record.setTimestamp(timestamp); record.setVehicleId(vehicleId); record.setActualServiceDate(serviceDate); record.setActualScheduleTime(actualScheduleTime); record.setActualDsc(dsc); record.setActualPhase(EVehiclePhase.IN_PROGRESS.toString()); task.addRecord(record); scheduleTime += 60 + random.nextGaussian() * 10; } return addTask(task); } /**** * Private Methods ****/ private int addTask(SimulatorTask task) { task.setId(_taskIndex.incrementAndGet()); task.setVehicleLocationService(_vehicleLocationInferenceService); _tasks.put(task.getId(), task); for (final Object tag : task.getTags()) { final List<SimulatorTask> tasks = getTasksForTag(tag, true); synchronized (tasks) { tasks.add(task); } } final Future<?> future = _executor.submit(task); task.setFuture(future); return task.getId(); } private List<SimulatorTask> getTasksForTag(Object tag, boolean create) { List<SimulatorTask> tasks = _tasksByTag.get(tag); if (tasks == null && create) { final ArrayList<SimulatorTask> newTasks = new ArrayList<SimulatorTask>(); tasks = _tasksByTag.putIfAbsent(tag, newTasks); if (tasks == null) tasks = newTasks; } return tasks; } private String generateBlockServiceDateTag(AgencyAndId blockId, long serviceDate) { return blockId.toString() + "-" + serviceDate; } private int getShiftStartTimeProperty(Properties properties) { int shiftStartTime = 0; if (properties.containsKey(ARG_SHIFT_START_TIME)) shiftStartTime = Integer.parseInt(properties.getProperty(ARG_SHIFT_START_TIME)) * 60; return shiftStartTime; } private AgencyAndId getVehicleIdProperty(Random random, Properties properties, String agencyId) { AgencyAndId vehicleId = null; if (properties.containsKey(ARG_VEHICLE_ID)) vehicleId = AgencyAndIdLibrary.convertFromString(properties.getProperty(ARG_VEHICLE_ID)); if (vehicleId == null) { final int vid = random.nextInt(9999); vehicleId = new AgencyAndId(agencyId, vid + ""); } return vehicleId; } private double getLocationSigma(Properties properties) { double locationSigma = 0.0; if (properties.containsKey(ARG_LOCATION_SIGMA)) locationSigma = Double.parseDouble(properties.getProperty(ARG_LOCATION_SIGMA)); return locationSigma; } private CoordinatePoint applyLocationNoise(double lat, double lon, double locationSigma, Random random) { if (locationSigma == 0) return new CoordinatePoint(lat, lon); final double latRadiusInMeters = random.nextGaussian() * locationSigma; final double lonRadiusInMeters = random.nextGaussian() * locationSigma; final CoordinateBounds bounds = SphericalGeometryLibrary.bounds(lat, lon, Math.abs(latRadiusInMeters), Math.abs(lonRadiusInMeters)); final double latRadius = (bounds.getMaxLat() - bounds.getMinLat()) / 2 * (latRadiusInMeters / Math.abs(latRadiusInMeters)); final double lonRadius = (bounds.getMaxLon() - bounds.getMinLon()) / 2 * (lonRadiusInMeters / Math.abs(lonRadiusInMeters)); return new CoordinatePoint(lat + latRadius, lon + lonRadius); } private SortedMap<Double, Integer> getScheduleDeviations(Properties properties) { final SortedMap<Double, Integer> scheduleDeviations = new TreeMap<Double, Integer>(); if (properties.containsKey(ARG_SCHEDULE_DEVIATIONS)) { final String raw = properties.getProperty(ARG_SCHEDULE_DEVIATIONS); for (final String token : raw.split(",")) { final String[] kvp = token.split("="); if (kvp.length != 2) throw new IllegalArgumentException("invalid schedule deviations spec: " + raw); final double ratio = Double.parseDouble(kvp[0]); final int deviation = Integer.parseInt(kvp[1]) * 60; scheduleDeviations.put(ratio, deviation); } } return scheduleDeviations; } private boolean isIncludeStartTime(Properties properties) { if (!properties.containsKey(ARG_INCLUDE_START)) return false; final String value = properties.getProperty(ARG_INCLUDE_START).toLowerCase(); return value.equals("true") || value.equals("yes") || value.equals("1"); } }