Skip to content

Commit

Permalink
Rock tour: keep driving time matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed May 25, 2018
1 parent 4e504fd commit 98da354
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 6 deletions.
Binary file modified optaplanner-examples/data/rocktour/unsolved/48shows.xlsx
Binary file not shown.
4 changes: 4 additions & 0 deletions optaplanner-examples/pom.xml
Expand Up @@ -176,6 +176,10 @@
</exclusion>
</exclusions>
</dependency>
<!--<dependency>-->
<!--<groupId>javax.json</groupId>-->
<!--<artifactId>javax.json-api</artifactId>-->
<!--</dependency>-->
</dependencies>

</project>
Expand Up @@ -126,6 +126,10 @@ protected String currentPosition() {
+ (currentRowNumber + 1) + CellReference.convertNumToColString(currentColumnNumber) + ")";
}

protected boolean hasSheet(String sheetName) {
return workbook.getSheet(sheetName) != null;
}

protected void nextSheet(String sheetName) {
currentSheet = workbook.getSheet(sheetName);
if (currentSheet == null) {
Expand Down
Expand Up @@ -16,12 +16,17 @@

package org.optaplanner.examples.rocktour.domain;

import java.util.Map;

public class RockLocation {

protected String cityName;
protected double latitude;
protected double longitude;

// Prefer Map over array or List because shows might be added and removed in real-time planning.
protected Map<RockLocation, Long> drivingSecondsMap;

public RockLocation() {
}

Expand All @@ -36,18 +41,20 @@ public RockLocation(String cityName, double latitude, double longitude) {
* @return a positive number, in seconds
*/
public long getDrivingTimeTo(RockLocation location) {
// TODO replace stub method by actual measurements
double distance = getAirDistanceDoubleTo(location);
// Multiplied by 1000 to avoid floating point arithmetic rounding errors
return (long) (distance * 1000.0 + 0.5);
if (this == location) {
return 0L;
}
return drivingSecondsMap.get(location);
}

public double getAirDistanceDoubleTo(RockLocation location) {
public long getAirDistanceTo(RockLocation location) {
// Euclidean distance (Pythagorean theorem) - not correct when the surface is a sphere
double latitudeDifference = location.latitude - latitude;
double longitudeDifference = location.longitude - longitude;
return Math.sqrt(
double distance = Math.sqrt(
(latitudeDifference * latitudeDifference) + (longitudeDifference * longitudeDifference));
// Multiplied by 1000 to avoid floating point arithmetic rounding errors
return (long) (distance * 1000.0 + 0.5);
}

@Override
Expand Down Expand Up @@ -83,4 +90,12 @@ public void setLongitude(double longitude) {
this.longitude = longitude;
}

public Map<RockLocation, Long> getDrivingSecondsMap() {
return drivingSecondsMap;
}

public void setDrivingSecondsMap(Map<RockLocation, Long> drivingSecondsMap) {
this.drivingSecondsMap = drivingSecondsMap;
}

}
Expand Up @@ -22,7 +22,9 @@
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Random;
import java.util.TreeSet;
Expand Down Expand Up @@ -95,9 +97,11 @@ private void createShowList(RockTourSolution solution, LocationDataGenerator.Loc
}
List<RockShow> showList = new ArrayList<>(locationDataArray.length);
long showId = 0L;
List<RockLocation> locationList = new ArrayList<>(locationDataArray.length);
for (int i = 0; i < locationDataArray.length; i++) {
LocationDataGenerator.LocationData locationData = locationDataArray[i];
RockLocation location = new RockLocation(locationData.getName(), locationData.getLatitude(), locationData.getLongitude());
locationList.add(location);
if (i == 0) {
RockBus bus = new RockBus();
bus.setId((long) i);
Expand Down Expand Up @@ -128,6 +132,16 @@ private void createShowList(RockTourSolution solution, LocationDataGenerator.Loc
showList.add(show);
}
}
for (int i = 0; i < locationList.size(); i++) {
RockLocation fromLocation = locationList.get(i);
Map<RockLocation, Long> drivingSecondsMap = new LinkedHashMap<>(locationList.size());
for (int j = 0; j < locationList.size(); j++) {
RockLocation toLocation = locationList.get(j);
long divingSeconds = fromLocation == toLocation ? 0L : random.nextInt(1000); // TODO not random
drivingSecondsMap.put(toLocation, divingSeconds);
}
fromLocation.setDrivingSecondsMap(drivingSecondsMap);
}
solution.setShowList(showList);
}

Expand Down
Expand Up @@ -26,11 +26,16 @@
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Stream;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
Expand Down Expand Up @@ -71,6 +76,7 @@ public RockTourSolution read() {
readConfiguration();
readBus();
readShowList();
readDrivingTime();
return solution;
}

Expand Down Expand Up @@ -210,6 +216,70 @@ private void readShowList() {
solution.setShowList(showList);
}

private void readDrivingTime() {
Map<Pair<Double, Double>, List<RockLocation>> latLongToLocationMap = Stream.concat(
Stream.of(solution.getBus().getStartLocation(), solution.getBus().getEndLocation()),
solution.getShowList().stream().map(RockShow::getLocation))
.distinct()
.sorted(Comparator.comparing(RockLocation::getLatitude).thenComparing(RockLocation::getLongitude).thenComparing(RockLocation::getCityName))
.collect(groupingBy(location -> Pair.of(location.getLatitude(), location.getLongitude()),
LinkedHashMap::new, toList()));
if (!hasSheet("Driving time")) {
latLongToLocationMap.forEach((fromLatLong, fromLocationList) -> {
for (RockLocation fromLocation : fromLocationList) {
fromLocation.setDrivingSecondsMap(new LinkedHashMap<>(fromLocationList.size()));
}
latLongToLocationMap.forEach((toLatLong, toLocationList) -> {
long drivingTime = 0L;
for (RockLocation fromLocation : fromLocationList) {
for (RockLocation toLocation : toLocationList) {
// TODO use haversine air distance and convert to average seconds for truck
drivingTime = fromLocation.getAirDistanceTo(toLocation);
fromLocation.getDrivingSecondsMap().put(toLocation, drivingTime);
}
}
});
});
return;
}
nextSheet("Driving time");
nextRow();
readHeaderCell("Driving time in seconds. Delete this sheet to generate it from air distances.");
nextRow();
readHeaderCell("Latitude");
readHeaderCell("");
for (Pair<Double, Double> latLong : latLongToLocationMap.keySet()) {
readHeaderCell(Double.toString(latLong.getLeft()));
}
nextRow();
readHeaderCell("");
readHeaderCell("Longitude");
for (Pair<Double, Double> latLong : latLongToLocationMap.keySet()) {
readHeaderCell(Double.toString(latLong.getRight()));
}
latLongToLocationMap.forEach((fromLatLong, fromLocationList) -> {
nextRow();
readHeaderCell(Double.toString(fromLatLong.getLeft()));
readHeaderCell(Double.toString(fromLatLong.getRight()));
for (RockLocation fromLocation : fromLocationList) {
fromLocation.setDrivingSecondsMap(new LinkedHashMap<>(fromLocationList.size()));
}
latLongToLocationMap.forEach((toLatLong, toLocationList) -> {
double drivingTimeDouble = nextNumericCell().getNumericCellValue();
long drivingTime = (long) drivingTimeDouble;
if (drivingTimeDouble != (double) drivingTime) {
throw new IllegalStateException(currentPosition() + ": The driving time (" + drivingTimeDouble
+ ") should be an integer number.");
}
for (RockLocation fromLocation : fromLocationList) {
for (RockLocation toLocation : toLocationList) {
fromLocation.getDrivingSecondsMap().put(toLocation, drivingTime);
}
}
});
});
}

}


Expand All @@ -236,6 +306,7 @@ public Workbook write() {
writeConfiguration();
writeBus();
writeShowList();
writeDrivingTime();
writeStopsView();
return workbook;
}
Expand Down Expand Up @@ -349,6 +420,53 @@ private void writeShowList() {
autoSizeColumnsWithHeader();
}

private void writeDrivingTime() {
nextSheet("Driving time", 2, 3, false);
nextRow();
nextHeaderCell("Driving time in seconds. Delete this sheet to generate it from air distances.");
currentSheet.addMergedRegion(new CellRangeAddress(currentRowNumber, currentRowNumber,
currentColumnNumber, currentColumnNumber + 10));
Map<Pair<Double, Double>, List<RockLocation>> latLongToLocationMap = Stream.concat(
Stream.of(solution.getBus().getStartLocation(), solution.getBus().getEndLocation()),
solution.getShowList().stream().map(RockShow::getLocation))
.distinct()
.sorted(Comparator.comparing(RockLocation::getLatitude).thenComparing(RockLocation::getLongitude).thenComparing(RockLocation::getCityName))
.collect(groupingBy(location -> Pair.of(location.getLatitude(), location.getLongitude()),
LinkedHashMap::new, toList()));
nextRow();
nextHeaderCell("Latitude");
nextHeaderCell("");
for (Pair<Double, Double> latLong : latLongToLocationMap.keySet()) {
nextHeaderCell(Double.toString(latLong.getLeft()));
}
nextRow();
nextHeaderCell("");
nextHeaderCell("Longitude");
for (Pair<Double, Double> latLong : latLongToLocationMap.keySet()) {
nextHeaderCell(Double.toString(latLong.getRight()));
}
latLongToLocationMap.forEach((fromLatLong, fromLocationList) -> {
nextRow();
nextHeaderCell(Double.toString(fromLatLong.getLeft()));
nextHeaderCell(Double.toString(fromLatLong.getRight()));
latLongToLocationMap.forEach((toLatLong, toLocationList) -> {
long drivingTime = fromLocationList.get(0).getDrivingTimeTo(toLocationList.get(0));
for (RockLocation fromLocation : fromLocationList) {
for (RockLocation toLocation : toLocationList) {
if (fromLocation.getDrivingTimeTo(toLocation) != drivingTime) {
throw new IllegalStateException("The driving time (" + drivingTime
+ ") from (" + fromLocationList.get(0) + ") to (" + toLocationList.get(0)
+ ") is not the driving time (" + fromLocation.getDrivingTimeTo(toLocation)
+ ") from (" + fromLocation + ") to (" + toLocation + ").");
}
}
}
nextCell().setCellValue(drivingTime);
});
});
autoSizeColumnsWithHeader();
}

private void writeStopsView() {
nextSheet("Stops", 1, 1, true);
nextRow();
Expand Down

0 comments on commit 98da354

Please sign in to comment.