使用pgrouting获取路线距离时遇到麻烦

时间:2013-09-09 23:10:24

标签: php ajax openlayers mapnik pgrouting

我从pgrouting-workshop文件开始,但是我无法使用多种方法获得准确的路径长度。

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

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

然后我想,我只是通过获得一段的开头和前一段的结尾之间的距离来总结间隙。但是,我发现这些段不会按顺序返回。

我受到了阻碍。如何使用pgrouting获得准确的路径长度?这是我使用的html和php脚本:

路由-final05.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);

?>

2 个答案:

答案 0 :(得分:0)

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

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

如果length不等于cost,则可以使用结果的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

P上。 S.我不知道您使用哪个应用程序来创建方法数据库,但我确信osm2po计算正确的方式。

答案 1 :(得分:0)

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

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