1.前言

众所周知,Graphhopper可以通过OSM数据或GTFS数据提供导航服务,对于其他的数据,Graphhopper是不支持的。
但是很多时候,用户想接入自己的数据进行导航,此时就需要进行一个数据的转化,才可以将用户数据接入Graphhopper来提供导航服务
而用户最常用的数据,就是shp类型的数据,接下来,我们通过shp类型的数据为例,来讲解如何将shp类型数据接入Graphhopper,大致可分为以下步骤

  1. 了解数据接入有哪些前置必备条件
  2. 了解OSM数据的格式以及示例
  3. 了解如何将shp数据转换为osm数据
  4. 测试转换后的osm数据准确性
  5. 将转换后的osm数据接入Graphhopper进行测试
  6. 以上方案的优化点

    2.数据接入前置必须条件

    数据坐标系要求

    矢量数据坐标系必须为WGS-84坐标系

    OSM可视化软件下载

    下载

    使用的软件名称为Josm(Java OpenStreetMap),是一款由Java编写,专门进行OSM编辑,查看,下载的软件
    下载地址:https://josm.openstreetmap.de/
    点击下图所示,即可下载
    image.png

    安装

    下载为.exe文件,直接点击运行即可。需要注意的是,,JOSM无法选择安装地址,默认安装到C盘!!!!这一点属实有点讨厌

    运行

    安装完成后,运行即可。如下图,点击文件->打开即可打开OSM文件
    image.png

    3.OSM数据示例

  7. 包含四个部分,header标签,node标签,way标签,relation标签

  8. node标签,way标签,relation标签都具有id,visible(可见性),user(作者),lon,lat等基本属性
  9. 可以通过tag标签对node、way、relation标签的属性进行属性描述
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <osm version="0.6" generator="CGImap 0.8.8 (3727084 spike-07.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
    3. <bounds minlat="25.0553000" minlon="121.5731000" maxlat="25.0634000" maxlon="121.5836000"/>
    4. <node id="661021630" visible="true" version="5" changeset="42553462" timestamp="2016-09-30T22:10:32Z" user="calfarome" uid="2511706" lat="25.0633286" lon="121.5806923">
    5. <tag k="highway" v="traffic_signals"/>
    6. </node>
    7. <node id="1838345068" visible="true" version="7" changeset="94533002" timestamp="2020-11-21T06:33:37Z" user="Supaplex" uid="274857" lat="25.0642896" lon="121.5758457">
    8. <tag k="amenity" v="atm"/>
    9. <tag k="operator" v="華南銀行"/>
    10. <tag k="operator:en" v="Hua Nan"/>
    11. </node>
    12. <node id="6336435728" visible="true" version="4" changeset="74199387" timestamp="2019-09-07T07:21:02Z" user="Supaplex" uid="274857" lat="25.0567279" lon="121.5817995">
    13. <tag k="description" v="台北市內湖區週美里"/>
    14. <tag k="emergency" v="fire_hydrant"/>
    15. <tag k="fire_hydrant:type" v="underground"/>
    16. <tag k="fire_hydrant:type:zh" v="地下式消防栓"/>
    17. <tag k="ref:DGNID" v="4747"/>
    18. <tag k="ref:UFID" v="3085"/>
    19. <tag k="source" v="臺北自來水事業處"/>
    20. </node>
    21. <way id="676750092" visible="true" version="1" changeset="68129856" timestamp="2019-03-14T09:31:43Z" user="xu3louisyi" uid="4819877">
    22. <nd ref="6337341000"/>
    23. <nd ref="6337340944"/>
    24. <nd ref="6337340943"/>
    25. <nd ref="6337340998"/>
    26. <nd ref="6337341000"/>
    27. <tag k="building:levels" v="4"/>
    28. <tag k="building:min_level" v="3"/>
    29. <tag k="building:part" v="yes"/>
    30. </way>
    31. <relation id="13235028" visible="true" version="2" changeset="114127108" timestamp="2021-11-23T07:31:53Z" user="stevennero" uid="14243003">
    32. <member type="relation" ref="13235026" role=""/>
    33. <member type="relation" ref="13235027" role=""/>
    34. <tag k="name" v="臺北市 南京幹線 南港高工—圓環"/>
    35. <tag k="name:en" v="Taipei City Nanjing Metro Bus Nangang Vocational High School-Taipei Circle"/>
    36. <tag k="name:zh" v="臺北市 南京幹線 南港高工—圓環"/>
    37. <tag k="network" v="臺北市市區公車"/>
    38. <tag k="network:en" v="Taipei Joint Bus System"/>
    39. <tag k="network:nan" v="Tâi-pak-chhī Chhī-khu Kong-chhia"/>
    40. <tag k="network:nan-HJ" v="臺北市市區公車"/>
    41. <tag k="network:nan-POJ" v="Tâi-pak-chhī Chhī-khu Kong-chhia"/>
    42. <tag k="network:nan-TL" v="Tâi-pak-tshiī Tshī-khu Kong-tshia"/>
    43. <tag k="network:wikidata" v="Q5972821"/>
    44. <tag k="network:wikipedia" v="zh:臺北市市區公車"/>
    45. <tag k="network:zh" v="臺北市市區公車"/>
    46. <tag k="old_ref" v="棕7"/>
    47. <tag k="old_ref:en" v="BR7"/>
    48. <tag k="opening_hours" v="Mo-Su 05:30-23:30"/>
    49. <tag k="operator" v="首都客運"/>
    50. <tag k="operator:en" v="Capital Bus"/>
    51. <tag k="ref" v="南京幹線"/>
    52. <tag k="ref:en" v="Nanjing Metro Bus"/>
    53. <tag k="route_master" v="bus"/>
    54. <tag k="type" v="route_master"/>
    55. </relation>
    56. </osm>

    4.SHP->OSM

    SHP文件转OSM文件目前网上推荐了2种方法,各有利弊,接下来开始逐一说明

    Perl脚本方法

    创建Perl文件

    创建名为shp2osm的pl文件。文件内容如下 ```perl

    Copyright (c) 2006 Gabriel Ebner ge@gabrielebner.at

    updated in 2008 by Tobias Wendorff tobias.wendorff@uni-dortmund.de

    HTML-Entities based on ideas of Hermann Schwärzler

    Gauß-Krüger implementation based on gauss.pl by Andreas Achtzehn

    version 1.3 (17. September 2008)

use Geo::ShapeFile; use HTML::Entities qw(encode_entities_numeric); use Math::Trig;

if(@ARGV == 0) { print “usage:\n”; print “with transformation from GK to WGS84: ‘shp2osm.pl shapefile GK’\n”; print “without transformation: ‘shp2osm.pl shapefile’”; exit; }

print <<’END’; <?xml version=’1.0’?>

END

BEGIN { our %default_tags = ( natural => ‘water’, source => ‘SWDB’ ); }

BEGIN { our %default_tags = ( ); }

my $file = @ARGV[0]; $file =~ s/.shp$//; my $shpf = Geo::ShapeFile->new($file); proc_shpf($shpf);

{ BEGIN { our $i = -1; }

  1. sub tags_out {
  2. my ($tags) = @_;
  3. my %tags = $tags ? %$tags : ();
  4. #$tags{'created_by'} ||= 'shp2osm.pl';
  5. delete $tags{'_deleted'} unless $tags{'_deleted'};
  6. while ( my ( $k, $v ) = each %tags ) {
  7. my $key = encode_entities_numeric($k);
  8. my $val = encode_entities_numeric($v);
  9. print ' <tag k="'. $key .'" v="'. $val ."\"/>\n" if $val;
  10. }
  11. }
  12. sub node_out {
  13. my ( $lon, $lat, $tags ) = @_;
  14. my $id = $i--;
  15. if(@ARGV[1] eq 'GK') {
  16. my ($wgs84lon, $wgs84lat) = gk2geo($lon, $lat);
  17. print " <node id='$id' visible='true' lat='$wgs84lat' lon='$wgs84lon' />\n";
  18. } else {
  19. print " <node id='$id' visible='true' lat='$lat' lon='$lon' />\n";
  20. }
  21. $id;
  22. }
  23. sub seg_out {
  24. my $id = $i+1;
  25. $id;
  26. }
  27. sub way_out {
  28. my ( $segs, $tags ) = @_;
  29. my $id = $i--;
  30. print " <way id='$id' visible='true'>\n";
  31. print " <nd ref='$_' />\n" for @$segs;
  32. tags_out $tags;
  33. print " </way>\n";
  34. $id;
  35. }

}

print <<’END’; END

sub polylineout { my ( $pts, $tags, $connect_last_seg ) = @;

  1. my ( $first_node, $last_node, @segs );
  2. for my $pt (@$pts) {
  3. my $node = node_out @$pt;
  4. push @segs, seg_out $last_node, $node;
  5. $last_node = $node;
  6. $first_node ||= $last_node;
  7. }
  8. push @segs, seg_out $last_node, $first_node
  9. if $first_node && $connect_last_seg;
  10. way_out \@segs, $tags;

}

sub procobj { my ( $shp, $dbf, $type ) = @; my $tags = { %defaulttags, %$dbf }; my $is_polygon = $type % 10 == 5; for ( 1 .. $shp->num_parts ) { polyline_out [ map( [ $->X(), $->Y() ], $shp->get_part($) ) ], $tags, $is_polygon; } }

sub procshpf { my ($shpf) = @; my $type = $shpf->shapetype; for ( 1 .. $shpf->shapes() ) { my $shp = $shpf->get_shp_record($); my %dbf = $shpf->getdbf_record($); proc_obj $shp, \%dbf, $type; } }

sub gk2geo { my $sr = $[0]; my $sx = $[1];

my $bm = int($sr/1000000); my $y = $sr-($bm1000000+500000); my $si = $sx/111120.6196; my $px = $si+0.143885358sin(2$si0.017453292)+0.00021079sin(4$si0.017453292)+0.000000423sin(6$si0.017453292); my $t = (sin($px0.017453292))/(cos($px0.017453292)); my $v = sqrt(1+0.006719219cos($px0.017453292)cos($px0.017453292)); my $ys = ($y$v)/6398786.85; my $lat = $px-$ys$ys57.29577$t$v$v(0.5-$ys$ys(4.97-3$t$t)/24); my $dl = $ys57.29577/cos($px0.017453292) (1-$ys$ys/6($v$v+2$t$t-$ys$ys(0.6+1.1$t$t)(0.6+1.1$t$t))); my $lon = $bm*3+$dl;

my $potsd_a = 6377397.155; my $wgs84_a = 6378137.0; my $potsd_f = 1/299.152812838; my $wgs84_f = 1/298.257223563;

my $potsd_es = 2$potsd_f - $potsd_f$potsd_f;

my $potsd_dx = 606.0; my $potsd_dy = 23.0; my $potsd_dz = 413.0; my $pi = 3.141592654; my $latr = $lat/180$pi; my $lonr = $lon/180$pi;

my $sa = sin($latr); my $ca = cos($latr); my $so = sin($lonr); my $co = cos($lonr);

my $bda = 1-$potsd_f;

my $delta_a = $wgs84_a - $potsd_a; my $delta_f = $wgs84_f - $potsd_f;

my $rn = $potsd_a / sqrt(1-$potsd_essin($latr)sin($latr)); my $rm = $potsd_a ((1-$potsd_es)/sqrt(1-$potsd_essin($latr)sin($latr)1-$potsd_essin($latr)sin($latr)1-$potsd_essin($latr)*sin($latr)));

my $ta = (-$potsd_dx$sa$co - $potsd_dy$sa$so)+$potsd_dz$ca; my $tb = $delta_a(($rn$potsd_es$sa$ca)/$potsd_a); my $tc = $delta_f($rm/$bda+$rn$bda)$sa*$ca; my $dlat = ($ta+$tb+$tc)/$rm;

my $dlon = (-$potsd_dx$so + $potsd_dy$co)/($rn*$ca);

my $wgs84lat = ($latr + $dlat)180/$pi; my $wgs84lon = ($lonr + $dlon)180/$pi;

return( $wgs84lon, $wgs84lat); }

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/28218714/1663833719550-8ba4b216-ac33-4561-a42f-d9b2aa0368b8.png#clientId=u4e11a8f6-8057-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=712&id=ue568caa7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1068&originWidth=1770&originalType=binary&ratio=1&rotation=0&showTitle=false&size=101990&status=done&style=none&taskId=u04be513a-a17b-4517-93c6-0d065369924&title=&width=1180)
  2. <a name="IaEqh"></a>
  3. #### 安装Perl运行环境
  4. 下载地址:[https://strawberryperl.com/](https://strawberryperl.com/),选择对应位数下载即可<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/28218714/1663833833792-fdb7cd71-9f64-4bc7-a602-9eb50715416f.png#clientId=u4e11a8f6-8057-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=916&id=u69e5f065&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1374&originWidth=2559&originalType=binary&ratio=1&rotation=0&showTitle=false&size=640859&status=done&style=none&taskId=u2108e4e2-d281-4dea-9aec-4138361a9e9&title=&width=1706)<br />下载后为msi文件,直接点击即可安装,一路next即可<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/28218714/1663833967727-ae2dc55c-d08d-48f1-95b7-869db0246a72.png#clientId=u4e11a8f6-8057-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=309&id=uf03cd490&margin=%5Bobject%20Object%5D&name=image.png&originHeight=463&originWidth=1256&originalType=binary&ratio=1&rotation=0&showTitle=false&size=44271&status=done&style=none&taskId=u27231adb-66b8-422a-a975-37d3263c5a3&title=&width=837.3333333333334)
  5. <a name="HTXZH"></a>
  6. #### 安装脚本运行依赖
  7. Perl运行环境安装完成后,还需安装脚本所需依赖<br />任意目录下打开CMD命令行工具,输入如下命令
  8. ```shell
  9. cpan Geo::ShapeFile

出现如下图,可以看到,安装完成
image.png

文件转换

  1. 将shp2osm.pl文件复制到shp文件目录下
  2. 在shp文件同目录下,打开CMD命令行
  3. 运行如下命令。其中*.shp代码目录下所有shp文件

    1. FOR /R .\ %G IN (*.shp) DO shp2osm.pl "%~dpnG" > "%~dpnG.osm"

    如下,文件转换完成
    image.png

    验证

    使用JOSM打开转换的OSM文件,如下,文件渲染正常
    image.png

    优点

  4. 便于操作,运行命令即可,无需额外安装各种软件

  5. 可进行批量操作,在需转化文件较多时,可快速转换批量文件

    缺点

  6. 该脚本文件内容版本较老,有可能不适配高版本OSM文件标椎

  7. 部分shp数据转换过程中会报错,具体原因不明
  8. 导出的文件,node节点与way节点乱序,无法接入Graphhopper

    JOSM插件方法

    安装opendata插件

    JOSM文件原本无法打开shp文件,需额外安装opendata插件才可查看shp文件

  9. 点击编辑->首选项

image.png

  1. 点击插件

image.png

  1. 搜索opendata,点击确认即可

image.png

  1. 等下下载完成后,重启JOSM即可。(插件下载过程非常缓慢,要有点心理准备,等着就好)

    打开shp文件

    安装完插件后,点击文件->打开,选择shp文件即可打开(打开过程会有点慢,要等等)
    image.png

    转存为OSM文件

  2. 点击文件->另存为

image.png

  1. 修改文件名后缀为.osm,并选择保存文件OSM项即可

image.png

  1. 如下,OSM文件存储成功

image.png

验证

使用JOSM打开刚刚转存的文件,如下,可以看到,OSM文件转储成功
image.png

优点

  1. 可以解决Perl脚本方法报错问题,截止到目前没遇到过shp文件转换异常

    缺点

  2. 对于批量文件的转换不如Perl脚本方法便利

    5.导入后的数据接入Graphhopper

    目前数据接入异常

    第一个异常原因

    导入数据所有tags皆为无效tags,导致路径数据导入时,被判定为无效,进而导致nodes数据无法获得路径关联,被判定为空点,最后出现异常
    最重要方法:

  3. OSMReader->preProcess

  4. OSMReader->writeOsmToGraph

    第二个异常原因

    经过源码修改后,屏蔽了tags以及id相关检查,但是因为没有tags,因此无法进行权重,速度,等相关的属性关联,导致查询无返回数据

    解决方法
  5. 了解osm数据的tags属性集

  6. 了解shp数据的属性信息
  7. 自己写一个shp转osm代码

    6.Shp数据介绍

    常见的Shp文件通常包括如下5种

  8. shp:图形格式,用于保存元素的几何实体

  9. shx: 图形索引文件,几何体位置索引,记录每一个几何体在shp文件之中的位置,能够加快向前或向后搜索一个几何体的效率。
  10. dbf: 属性数据格式,以dBase IV的数据表格式存储每个几何形状的属性数据。
  11. prj:用于保存地理坐标系统与投影信息,是一个存储well-known text投影描述符的文本文件
  12. cpg:用于描述.dbf文件的代码页,指明其使用的字符编码

image.png