/* The MIT License (MIT) * * Copyright (c) 2015 Reinventing Geospatial, Inc. * * 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. */ import com.rgi.geopackage.GeoPackage; import com.rgi.geopackage.extensions.implementation.BadImplementationException; import com.rgi.geopackage.extensions.network.AttributeDescription; import com.rgi.geopackage.extensions.network.AttributedNode; import com.rgi.geopackage.extensions.network.AttributedType; import com.rgi.geopackage.extensions.routing.GeoPackageRoutingExtension; import com.rgi.geopackage.extensions.routing.RoutingNetworkDescription; import com.rgi.geopackage.verification.ConformanceException; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Random; /** * @author Luke Lambert * @author Mary Carome * */ @SuppressWarnings({"javadoc", "unused"}) public final class RouteTests { private static final int seed = 123456789; private static final int routeIterations = 100; private static final double nanoToSecond = 1.0e9; private static final double load = 40.0; private static final double weight = 85.0; private static final double velocity = 0.7; private static final double terrainFactor = 1.0; private static final double PECONSTANT1 = 1.5 * RouteTests.weight + 2.0*(RouteTests.weight + RouteTests.load)*(RouteTests.load / RouteTests.weight)*(RouteTests.load / RouteTests.weight) + 1.5* RouteTests.velocity * RouteTests.velocity * RouteTests.terrainFactor * RouteTests.weight + 1.5* RouteTests.velocity * RouteTests.velocity * RouteTests.terrainFactor * RouteTests.load; private static final double PECONSTANT2 = 0.35* RouteTests.velocity * RouteTests.terrainFactor; private static final double CFConstant1 = (RouteTests.velocity * RouteTests.terrainFactor *(RouteTests.weight + RouteTests.load))/3.5; private static final double CFConstant2 = (RouteTests.terrainFactor *(RouteTests.weight + RouteTests.load))/ RouteTests.weight; private static final double CFConstant3 = 25.0* RouteTests.terrainFactor * RouteTests.velocity * RouteTests.velocity; private RouteTests() { } public static void main(final String[] args) { // try(final GeoPackage gpkg = new GeoPackage(new File("routing_networks.gpkg"), GeoPackage.OpenMode.Open)) // { // final GeoPackageRoutingExtension routingExtension = gpkg.extensions().getExtensionImplementation(GeoPackageRoutingExtension.class); // // final RoutingNetworkDescription routingNetwork = routingExtension.getRoutingNetworkDescription("contour_1"); // // final AttributeDescription longitudeAttribute = routingNetwork.getLongitudeDescription(); // final AttributeDescription latitudeAttribute = routingNetwork.getLatitudeDescription(); // final AttributeDescription elevationAttribute = routingNetwork.getElevationDescription(); // // final Route route = routingExtension.aStar(routingNetwork, // 1678, // 36553, // Arrays.asList(longitudeAttribute, // latitudeAttribute, // elevationAttribute), // Collections.emptyList(), // edgeParams -> distance(edgeParams.getFrom(), edgeParams.getTo()), // RouteTests::distance2, // null, // null); // // System.out.println(route.getNodesAttributes().size()); // } // catch(final ClassNotFoundException | SQLException | ConformanceException | IOException | BadImplementationException ex) // { // ex.printStackTrace(); // } try(final GeoPackage gpkg = new GeoPackage(new File("routing_networks.gpkg"), GeoPackage.OpenMode.Open)) { final GeoPackageRoutingExtension routingExtension = gpkg.extensions().getExtensionImplementation(GeoPackageRoutingExtension.class); //testNetworks(routingExtension, routingExtension.getRoutingNetworkDescriptions()); testNetworks(routingExtension, Collections.singletonList(routingExtension.getRoutingNetworkDescription("mwtc_pandolf"))); } catch(final ClassNotFoundException | SQLException | ConformanceException | IOException | BadImplementationException ex) { ex.printStackTrace(); } } private static void testNetworks(final GeoPackageRoutingExtension routingExtension, final Iterable<RoutingNetworkDescription> routingNetworks) { routingNetworks.forEach(routingNetwork -> { try { final double seconds = testNetwork(routingExtension, routingNetwork); System.out.format("%s finished %d routes in %f seconds\n", routingNetwork.getNetwork().getTableName(), routeIterations, seconds); } catch(final SQLException e) { e.printStackTrace(); } }); } private static double testNetwork(final GeoPackageRoutingExtension routingExtension, final RoutingNetworkDescription routingNetwork) throws SQLException { final Random rand = new Random(seed); final int maxNodeIdentifier = routingExtension.getNetworkExtension() .getNodeCount(routingNetwork.getNetwork()); final AttributeDescription costAttribute = routingExtension.getNetworkExtension() .getAttributeDescription(routingNetwork.getNetwork(), "cost_pandolf", AttributedType.Edge); if(costAttribute == null) { return 0.0; } final AttributeDescription longitudeAttribute = routingNetwork.getLongitudeDescription(); final AttributeDescription latitudeAttribute = routingNetwork.getLatitudeDescription(); final AttributeDescription elevationAttribute = routingNetwork.getElevationDescription(); final Collection<AttributeDescription> nodeAttributes = Arrays.asList(longitudeAttribute, latitudeAttribute, elevationAttribute); double totalTime = 0; for(int x = 0; x < routeIterations; ++x) { final long startTime = System.nanoTime(); routingExtension.aStar(routingNetwork, rand.nextInt(maxNodeIdentifier), rand.nextInt(maxNodeIdentifier), nodeAttributes, Collections.emptyList(), attributedEdge -> RouteTests.getCaloricCost(attributedEdge.getFromNode(), attributedEdge.getToNode()), RouteTests::getCaloricCost, null, null); totalTime += (System.nanoTime() - startTime); } return (totalTime / nanoToSecond) / routeIterations; // Average number of seconds per route call } /** * Pandolf Caloric equation found at following link * https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB4QFjAAahUKEwiE96OIwbXHAhUGOj4KHa2-BZs&url=http%3A%2F%2Fweb.stanford.edu%2F~clint%2FRun_Walk2004a.rtf&ei=_KbUVYSJOYb0-AGt_ZbYCQ&usg=AFQjCNE3KbcRBmdb04KbkMyU5UnOYh-U0Q&sig2=vg108EAL0net65gcbIsUFw&bvm=bv.99804247,d.cWw * http://www.researchgate.net/publication/279517297_Comparative_Analysis_of_Metabolic_Cost_Equations_A_Review * W= weight * L = load in kilograms * G = grade (%) * V = velocity (meters/second) * T = terrian factor * Terrain factor categories * 1.0 = blacktop road or treadmill * 1.1 = dirt road * 1.2 = light brush * 1.5 = heavy brush * 1.8 = swampy bog * 2.1 = loose sand * 2.5 = soft snow (15cm depth) * 3.3 = soft snow (25 cm depth) * 4.1 = soft snow (35 cm depth) * M = Metabolic rate (in Watts) * CF = correction factor (accounting for metabolic rate for down hill) * PE = pandolf equation * * Our Simplified Version of PE * * PE = PEConstant1 + PEConstant2(G*L + G*W) * * PEConstant1 = 1.5*W + 2.0(W + L)*(1/W)(1/W) + 1.5*V*V*T*W + 1.5*V*V*T*L * PEConstant2 = 0.35*V*T * * * Derived from: * * PE = 1.5*W + 2.0(W + L)*(1/W)(1/W) + T(W + L)*(1.5*V*V + 0.35*V*G) * 1.5*W + 2.0(W + L)*(1/W)(1/W) + (T*W + T*L)*(1.5*V*V + 0.35*V*G) * 1.5*W + 2.0(W + L)*(1/W)(1/W) + 1.5*V*V*T*W + 1.5*V*V*T*L + 0.35*V*G*T*L + 0.35*V*G*T*W * PEConstant1 + 0.35*V*T(G*L + G*W) * PEConstant1 + PEConstant2(G*L + G*W) * * Our Simplified Version CF * * CF = G*CFConstant1 - (G + 6)(G + 6)*CFConstant2 + CFContstant3 * * CFConstant1 = (V*T(W+L))/3.5 * CFConstant2 = (T(W+L))/W * CFConstant3 = 25*T*V*V * * Derived from: * * CF = T[((G(W + L)*V)/3.5) + ((W + L)(G + 6)^2)/W) + 25*V*V)] * (T*(G(W + L)*V)/3.5) + (T*(W + L)(G + 6)^2)/W) + 25*T*V*V) * G[(V*T(W + L))/3.5] + (G + 6)^2[T * (W + L)/W] + 25*T*V*V * G[CFConstant1] + (G + 6)^2[CFConstant2] + CFConstant3 * * * @param fromNode start node * @param toNode end node * @return caloric cost to traverse between those two nodes */ private static double getCaloricCost(final AttributedNode fromNode, final AttributedNode toNode) { final double grade = RouteTests.getGrade(fromNode, toNode); final double pandolfEquation = RouteTests.PECONSTANT1 + RouteTests.PECONSTANT2 * (grade * RouteTests.load + grade * RouteTests.weight); double correctionFactor = 0.0; //if downhill if(grade < 0.0) { correctionFactor =grade * RouteTests.CFConstant1 - (grade + 6.0)*(grade + 6.0)* RouteTests.CFConstant2 + RouteTests.CFConstant3; } final double metabolicRate = pandolfEquation-correctionFactor;//watts return metabolicRate * RouteTests.getDistance(fromNode, toNode)/4184.0/ RouteTests.velocity; } /** * Returns the percentage of grade traversing between two nodes * @param fromNode the node traveling from * @param toNode the node traveling to * @return percentage of grade traversing two nodes */ private static double getGrade(final AttributedNode fromNode, final AttributedNode toNode) { final double longitude = (Double)toNode.getAttribute(0) - (Double)fromNode.getAttribute(0); final double latitude = (Double)toNode.getAttribute(1) - (Double)fromNode.getAttribute(1); final double elevation = (Double)toNode.getAttribute(2) - (Double)fromNode.getAttribute(2); return 100.0 * elevation/(Math.sqrt(longitude*longitude + latitude*latitude)); } private static double getDistance(final AttributedNode fromNode, final AttributedNode toNode) { final double longitude = (Double)toNode.getAttribute(0) - (Double)fromNode.getAttribute(0); final double latitude = (Double)toNode.getAttribute(1) - (Double)fromNode.getAttribute(1); final double elevation = (Double)toNode.getAttribute(2) - (Double)fromNode.getAttribute(2); return Math.sqrt(latitude * latitude + longitude * longitude + elevation * elevation); } }