使用灌浆获取路线距离时遇到问题



我从 pgrouting-workshop 文件开始,但我在使用多种方法获得准确的路线长度时遇到了麻烦。

第一种方法只是将从 ajax 调用返回的长度变量相加到 pgrouting.php 脚本。 从路由数据库返回的长度似乎是从距离公式获得的:sqrt((x2-x1)^2 + (y2-y1)^s),其中 x 坐标是经度,y 坐标是 epsg:4326 中的纬度。 由于纬度和经度在投影到地球表面时长度不同,我不太确定这个值有什么好处,但我是这个游戏的新手,所以......

由于第一种方法没有提供准确的总距离,因此我决定使用用于长度计算的haversine公式对从ajax调用返回的每个段的长度求和。 然而,我将这个长度与起点和终点之间的"乌鸦飞翔"(ATCF)距离进行了比较,发现这个长度小于ATCF长度。 因此,我将这些路线段中的每一个都添加到开放层中的单独矢量层中,发现这些路段并未覆盖整个路线。 许多路段缺失,特别是在路线的弯曲部分。

所以后来我想,好吧,我就通过获取一个段的开头和前一段的结束之间的距离来总结差距。 但是,我发现这些段没有按顺序返回。

我愣住了。 如何使用灌浆获得准确的路线长度? 以下是我使用的 html 和 php 脚本:

路由决赛05.html:

<html>
<head>
<title>A Basic GeoExt Page</title>
<script src="/scripts/openlayers/OpenLayers.js" type="text/javascript"></script>
<style type="text/css">
  html, body { height: 100%; }
  body {margin: 0px;}
  #map {
    width: 100%;
    height: 90%;
  }
</style>
<script src="DrawPoints.js" type="text/javascript"></script>
<script src="proj4js/lib/proj4js.js" type="text/javascript"></script>
<script type="text/javascript">
        // global projection objects (uses the proj4js lib)
        var epsg_4326 = new OpenLayers.Projection("EPSG:4326"),
             epsg_900913 = new OpenLayers.Projection("EPSG:900913");
        function pgrouting(layer, method) {
                if (layer.features.length == 2) 
                {
                    route_layer.removeAllFeatures();
                    crow_layer.removeAllFeatures();
                    verify1_layer.removeAllFeatures();
                    verify2_layer.removeAllFeatures();
                    var startpoint = layer.features[0].geometry.clone();
                    startpoint.transform(epsg_900913, epsg_4326);
                    var finalpoint = layer.features[1].geometry.clone();
                    finalpoint.transform(epsg_900913, epsg_4326);
                    var result = {
                            startpoint: startpoint.x + " " + startpoint.y,
                            finalpoint: finalpoint.x + " " + finalpoint.y,
                            method: method,
                    };
//                  layer.removeFeatures(layer.features);
                    var params = OpenLayers.Util.getParameterString(result);
                    OpenLayers.loadURL("./php/pgrouting05.php?" + params,
                                                         '',
                                                         null,
                                                         drawRoute);

             }
        }
        function init(){
                map = new OpenLayers.Map('map', {
                        controls: [
                                new OpenLayers.Control.Navigation(),
                                new OpenLayers.Control.PanZoomBar(),
                                new OpenLayers.Control.LayerSwitcher({'ascending':true}),
                                //new OpenLayers.Control.Permalink(),
                                //new OpenLayers.Control.ScaleLine(),
                                //new OpenLayers.Control.Permalink('permalink'),
                                new OpenLayers.Control.MousePosition(),
                                new OpenLayers.Control.OverviewMap(),
                                new OpenLayers.Control.KeyboardDefaults(),
                                new OpenLayers.Control.Scale({'geodesic':true})
                        ],
                        //numZoomLevels: 6,
                        maxResolution: 156543.0339,
                        units: 'm',
                        projection: new OpenLayers.Projection("EPSG:900913"),
        //                 displayProjection: new OpenLayers.Projection("EPSG:4326"),
                        displayProjection: new OpenLayers.Projection("EPSG:900913"),
                        maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34)
                });
                // TileLite by default mounts on localhost port 8000 
                var tiles_url = "http://localhost:8000/";
                var tilelite_layer = new OpenLayers.Layer.OSM("Mapnik locally via TileLite", tiles_url + '${z}/${x}/${y}.png');
                map.addLayer(tilelite_layer);
                var lonLat = new OpenLayers.LonLat(-104.99323, 39.74259).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
                map.setCenter (lonLat, 14);
                // create the layer where the route will be drawn
                route_layer = new OpenLayers.Layer.Vector("route", {
                        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
                                strokeColor: "#ff9933",
                                strokeWidth: 3
                        }))
                });
                // create the layer where the "as the crow flies" route will be drawn
                crow_layer = new OpenLayers.Layer.Vector("crow", {
                        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
                                strokeColor: "#000000",
                                strokeWidth: 3
                        }))
                });
                // create a verification layer 
                verify1_layer = new OpenLayers.Layer.Vector("verify1", {
                        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
                                strokeColor: "#FF0000",
                                strokeWidth: 3
                        }))
                });
                // create a verification layer 
                verify2_layer = new OpenLayers.Layer.Vector("verify2", {
                        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
                                strokeColor: "#00FF00",
                                strokeWidth: 3
                        }))
                });
                // create the layer where the start and final points will be drawn
                points_layer = new OpenLayers.Layer.Vector("points");
                // when a new point is added to the layer, call the pgrouting function
                points_layer.events.on({
                        featureadded: function() {
                //                 pgrouting(store, points_layer, method.getValue());
                                pgrouting(points_layer, method);
                        }
                });
                // add the layers to the map
                map.addLayers([points_layer, route_layer, crow_layer, verify1_layer, verify2_layer]);
                // create the control to draw the points (see the DrawPoints.js file)
                var draw_points = new DrawPoints(points_layer);
                // create the control to move the points
                var drag_points = new OpenLayers.Control.DragFeature(points_layer, {
                        autoActivate: true
                });
                // when a point is moved, call the pgrouting function
                drag_points.onComplete = function() {
                //               pgrouting(store, points_layer, method.getValue());
                //               pgrouting(store, points_layer, method);
                                            pgrouting(points_layer, method);
                };
                // add the controls to the map
                map.addControls([draw_points, drag_points]);


                method = "SPS";

        };  // end init()
        function change_method()
        {
//          console.log(document);
            method = document.forms["f1"]["method_select"].value
            pgrouting(points_layer, method);
//          calc_dist();
        }
        function calc_dist()
        {
                console.log("entered calc_dist()")
                console.log(map.layers[2])
                console.log(map.layers[2].features[0])
        }
        function haversine_dist(lon1,lat1,lon2,lat2,unit)
        {
            var radius_km = 6371
            var radius_mi = 3958.75 
            var dlat = (lat2-lat1)*Math.PI/180.0;
            var dlon = (lon2-lon1)*Math.PI/180.0;
            var a = Math.sin(dlat/2) * Math.sin(dlat/2) + Math.cos(lat1*Math.PI/180.0) * Math.cos(lat2*Math.PI/180.0) * Math.sin(dlon/2) * Math.sin(dlon/2)
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
            if (unit == 'mi')
            {
                var d = radius_mi * c
            }
            else if (unit == 'km')
            {
                var d = radius_km * c
            }
            return d;
        }
        function drawRoute(response)
        {
            // interpret geojson data coming in from ajax response
            var geojson_format900913 = new OpenLayers.Format.GeoJSON({
                                                        internalProjection: epsg_900913,
                                                        externalProjection: epsg_4326
                                                });
            var geojson_format4326 = new OpenLayers.Format.GeoJSON();
            var geom4326 = geojson_format4326.read(response.responseText)
            var geom900913 = geojson_format900913.read(response.responseText)
//          console.log(geom900913);
//          console.log(geom4326);
            // add features
            route_layer.addFeatures(geom900913);
            // calculate distances between current segment start and previous segment end
            var lon1,lat1,lon2,lat2,seg_len;
            var pointList = [];
            var p1,p2;
            var dist2 = 0;
            for (i=0;i<geom900913.length;i++)
            {
                    if (i == 0)
                    {
                            var startpoint = points_layer.features[0].geometry.clone();
//                          var startpoint = points_layer.features[1].geometry.clone();
                            lon1 = startpoint.x;
                            lat1 = startpoint.y;
                            lon2 = geom900913[i].geometry.components[0].x;
                            lat2 = geom900913[i].geometry.components[0].y;
                    }
                    else
                    {
                            lon1 = geom900913[i-1].geometry.components[1].x
                            lat1 = geom900913[i-1].geometry.components[1].y
                            lon2 = geom900913[i].geometry.components[0].x
                            lat2 = geom900913[i].geometry.components[0].y

                    }
                    seg_len = haversine_dist(lon1,lat1,lon2,lat2,'mi');
                    if ( seg_len > 0 )
                    {
                            dist2 += seg_len;   
                            var pointList = [];
                            var p1 = new OpenLayers.Geometry.Point(lon1,lat1);
                            var p2 = new OpenLayers.Geometry.Point(lon2,lat2);
                            pointList.push(p1);
                            pointList.push(p2);
                            var route_seg = new OpenLayers.Feature.Vector(
                                        new OpenLayers.Geometry.LineString(pointList),null,null);
                            verify2_layer.addFeatures([route_seg]);
                    }
            }
//          verify2_layer.addFeatures(geom4326.transform(epsg_4326, epsg_900913));
            var sp900913 = points_layer.features[0].geometry.clone();
            var sp4326 = points_layer.features[0].geometry.clone();
            sp4326.transform(epsg_900913, epsg_4326);
            var fp900913 = points_layer.features[1].geometry.clone();
            var fp4326 = points_layer.features[1].geometry.clone();
            fp4326.transform(epsg_900913, epsg_4326);
//          console.log(sp4326);
//          console.log(fp4326);
            var lengths = [];
            var tot_length = 0.0;
//          console.log(tot_length)
            var lon1,lat1,lon2,lat2;
            for (i=0;i<geom4326.length;i++)
            {
//              lengths.push(geom4326[i].data.length);
                lon1 = geom4326[i].geometry.components[0].x
                lat1 = geom4326[i].geometry.components[0].y
                lon2 = geom4326[i].geometry.components[1].x
                lat2 = geom4326[i].geometry.components[1].y
                tot_length += parseFloat(haversine_dist(lon1,lat1,lon2,lat2,'mi'))
//              console.log(tot_length)
                var pointList = [];
                var p1 = new OpenLayers.Geometry.Point(lon1,lat1).transform(epsg_4326, epsg_900913);
                var p2 = new OpenLayers.Geometry.Point(lon2,lat2).transform(epsg_4326, epsg_900913);
                pointList.push(p1);
                pointList.push(p2);
    //          console.log(pointList)
                var route_seg = new OpenLayers.Feature.Vector(
                      new OpenLayers.Geometry.LineString(pointList),null,null);
    //          console.log(crow_line)
                verify1_layer.addFeatures([route_seg]);
            }
//          console.log(lengths);
//          console.log("sum of the parts: " + tot_length);
            // as the crow flies
            lon1 = sp4326.x
            lat1 = sp4326.y
            lon2 = fp4326.x
            lat2 = fp4326.y
            var line_length_mi = haversine_dist(lon1,lat1,lon2,lat2,'mi')
            var line_length_km = haversine_dist(lon1,lat1,lon2,lat2,'km')
//          console.log("straight line dist: " + line_length);
            document.getElementById("dist").innerHTML = "Distance: " + Math.round(tot_length*1000)/1000 + " miles"
            document.getElementById("straight_dist").innerHTML = "As the crow flies: " + Math.round(line_length_mi*1000)/1000 + " miles, " + Math.round(line_length_km*1000)/1000 + " km"
            var pointList = [];
            pointList.push(new OpenLayers.Geometry.Point(sp900913.x,sp900913.y));
            pointList.push(new OpenLayers.Geometry.Point(fp900913.x,fp900913.y));
//          console.log(pointList)
            var crow_line = new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.LineString(pointList),null,null);
//          console.log(crow_line)
            crow_layer.addFeatures([crow_line]);

        }
</script>
</head>
<body onload="init()">
<form name="f1">
    <table width="100%">
        <tr>
            <td align="center">
                <select name="method_select" onchange="change_method();">
                    <option value="SPD">Shortest Path Dijkstra</option>
                    <option value="SPA">Shortest Path A*</option>
                    <option value="SPS">Shortest Path Shooting*</option>
                </select>
            </td>
            <td align="center">
                <p id="dist"></p>
            </td>
            <td align="center">
                <p id="straight_dist"></p>
            </td>
        </tr>
    </table>
</form>
<div id="map" ></div>
<div id="gxmap"></div>
<div id="method"></div>

</body>
</html>

pgrouting.php(我在这里没有改变任何东西,这是直接从研讨会文件中)

<?php
        // Database connection settings
        define("PG_DB"  , "routing");
        define("PG_HOST", "localhost"); 
        define("PG_USER", "postgres");
        define("PG_PORT", "5432"); 
        define("TABLE",   "ways");
        //echo($_REQUEST)
        // Retrieve start point
        $start = preg_split('/ /',$_REQUEST['startpoint']);
        $startPoint = array($start[0], $start[1]);
        // Retrieve end point
        //$end = split(' ',$_REQUEST['finalpoint']);
        $end = preg_split('/ /',$_REQUEST['finalpoint']);
        $endPoint = array($end[0], $end[1]);


        //echo($end[0]);
?>
<?php
   // Find the nearest edge
   $startEdge = findNearestEdge($startPoint);
   $endEdge   = findNearestEdge($endPoint);
   // FUNCTION findNearestEdge
   function findNearestEdge($lonlat) {
      // Connect to database
      $con = pg_connect("dbname=".PG_DB." host=".PG_HOST." user=".PG_USER);
      $sql = "SELECT gid, source, target, the_geom, 
                  distance(the_geom, GeometryFromText(
                       'POINT(".$lonlat[0]." ".$lonlat[1].")', 4326)) AS dist 
                 FROM ".TABLE."  
                 WHERE the_geom && setsrid(
                       'BOX3D(".($lonlat[0]-0.1)." 
                              ".($lonlat[1]-0.1).", 
                              ".($lonlat[0]+0.1)." 
                              ".($lonlat[1]+0.1).")'::box3d, 4326) 
                 ORDER BY dist LIMIT 1";
      $query = pg_query($con,$sql);  
      $edge['gid']      = pg_fetch_result($query, 0, 0);  
      $edge['source']   = pg_fetch_result($query, 0, 1);  
      $edge['target']   = pg_fetch_result($query, 0, 2);  
      $edge['the_geom'] = pg_fetch_result($query, 0, 3);  
      // Close database connection
      pg_close($con);
      return $edge;
   }
?>
<?php
   // Select the routing algorithm
   switch($_REQUEST['method']) {
      case 'SPD' : // Shortest Path Dijkstra 
        $sql = "SELECT rt.gid, ST_AsGeoJSON(rt.the_geom) AS geojson, 
                     length(rt.the_geom) AS length, ".TABLE.".gid 
                  FROM ".TABLE.", 
                      (SELECT gid, the_geom 
                          FROM dijkstra_sp_delta(
                              '".TABLE."',
                              ".$startEdge['source'].",
                              ".$endEdge['target'].",
                              0.1)
                       ) as rt 
                  WHERE ".TABLE.".gid=rt.gid;";
        break;
      case 'SPA' : // Shortest Path A* 
        $sql = "SELECT rt.gid, ST_AsGeoJSON(rt.the_geom) AS geojson, 
                       length(rt.the_geom) AS length, ".TABLE.".gid 
                    FROM ".TABLE.", 
                        (SELECT gid, the_geom 
                            FROM astar_sp_delta(
                                '".TABLE."',
                                ".$startEdge['source'].",
                                ".$endEdge['target'].",
                                0.1)
                         ) as rt 
                    WHERE ".TABLE.".gid=rt.gid;";  
        break;
      case 'SPS' : // Shortest Path Shooting*
        $sql = "SELECT rt.gid, ST_AsGeoJSON(rt.the_geom) AS geojson, 
                       length(rt.the_geom) AS length, ".TABLE.".gid 
                    FROM ".TABLE.", 
                        (SELECT gid, the_geom 
                            FROM shootingstar_sp(
                                '".TABLE."',
                                ".$startEdge['gid'].",
                                ".$endEdge['gid'].",
                                0.1, 'length', true, true)
                         ) as rt 
                    WHERE ".TABLE.".gid=rt.gid;";
        break;   
   } // close switch
   // Connect to database
   $dbcon = pg_connect("dbname=".PG_DB." host=".PG_HOST." user=".PG_USER);
   // Perform database query
   $query = pg_query($dbcon,$sql); 
?>
<?php
   // Return route as GeoJSON
   $geojson = array(
      'type'      => 'FeatureCollection',
      'features'  => array()
   ); 
   // Add edges to GeoJSON array
   while($edge=pg_fetch_assoc($query)) {  
      $feature = array(
         'type' => 'Feature',
         'geometry' => json_decode($edge['geojson'], true),
         'crs' => array(
            'type' => 'EPSG',
            'properties' => array('code' => '4326')
         ),
         'properties' => array(
            //'osm_id' => $edge['osm_id'],
            'length' => $edge['length']
            //'x1' => $edge['x1']
         )
      );
      // Add feature array to feature collection array
      array_push($geojson['features'], $feature);
   }

   // Close database connection
   pg_close($dbcon);
   // Return routing result
   header('Content-type: application/json',true);
   echo json_encode($geojson);
?>

我没有检查 pgRouting 研讨会,但"从路由数据库返回的长度"可能是成本,因为成本是从 pgRouting SQL 调用返回的,请参阅文档。仅当您在 pgRouting 函数的 SQL 参数中使用实际长度作为成本时,成本值的总和才等于实际长度。

假设您在方法数据库中预先计算了正确的长度并使用长度作为成本,则可以使用第一种方法获得准确的长度。

如果长度不等于成本,您可以使用结果edge_id列,并以某种方式获取edge_id中包含 id 的所有方式的长度总和。 像这样的东西(未测试):

SELECT sum(length) FROM my_ways WHERE id IN (
   SELECT edge_id FROM shortest_path_astar("SQL", source, target, false, false)
) as edge_ids

附言我不知道您使用哪个应用程序来创建方式数据库,但我确信osm2po计算出正确的方式长度。

长度错误源于 sql 语句的这一部分:length(rt.the_geom) AS length这应该更改为 仅length . 我不太确定length(rt.the_geom) AS length做什么,但正如我在原始问题中所述,这似乎使用了距离公式。

如果使用 geojson 返回的路线段,则路线中仍然存在间隙,但与原始问题中描述的"乌鸦飞行距离"(沿直线路线)相比,总路线距离(使用修改后的长度)现在精确到 8 米以内。 这必然意味着某些线段的几何中的 x 和 y 值是错误的,从而导致间隙。

相关内容

最新更新