2013-04-21 184 views
1

我爲一個小區域下載了打開的街道地圖數據,我想過濾數據以獲取具有特殊類別的節點。OSM數據解析以獲取帶有子節點的節點

這裏是OSM數據

<node id="505126369" lat="31.2933856" lon="34.2687443" user="JumpStart International" uid="125156" visible="true" version="1" changeset="2568758" timestamp="2009-09-22T13:05:10Z"/> 
<node id="505126372" lat="31.2682934" lon="34.2745680" user="JumpStart International" uid="125156" visible="true" version="1" changeset="2568758" timestamp="2009-09-22T13:05:10Z"/> 
<node id="505126375" lat="31.2953082" lon="34.3471630" user="JumpStart International" uid="125156" visible="true" version="1" changeset="2568758" timestamp="2009-09-22T13:05:10Z"/> 
<node id="505126378" lat="31.2807872" lon="34.2757999" user="JumpStart International" uid="125156" visible="true" version="1" changeset="2568758" timestamp="2009-09-22T13:05:11Z"> 
    <tag k="amenity" v="school"/> 
    <tag k="name" v="Al Aqqad Basic &amp; Secondary Female School"/> 
    <tag k="name:ar" v="مدرسة العقاد الأساسية والثانوية للبنات"/> 
    </node> 

我想整個學校的樣品,在數據的醫院。

如果有人用PHP或Java進行XML解析,我將非常感謝與我和所有興趣分享它。

編輯 下面是一個簡單的開始我剛纔

$dataFile = base_url() . 'media/files/osmdata/map_3.xml'; 
    //echo ($dataFile); 

    $xml = simplexml_load_file($dataFile); 

    // $countTotal = count($xml->node); 
    // echo 'here'.$countTotal; 
    foreach ($xml as $key => $val) { 
     var_dump($val); 
       // can't manage things overs here 

    } 
+0

你堅持在看什麼部分的過程中,有什麼不爲你工作?當Google搜尋'php parse osm'(當然,'php parse xml')時,我會看到一些有趣的鏈接。爲什麼不先查看那些? – 2013-04-21 08:12:17

+0

我開始製作我自己的XML解析器,但我對XML不太熟悉。我堅持循環遍歷節點,只得到父節點。 – palAlaa 2013-04-21 08:16:07

+1

@palAlaa:請問您如何使用PHP來顯示問題。然後我們可以給你一些提示和/或給你一個答案。否則你的描述有點寬泛,我會猜測你遇到困難以及如何解決問題。 – hakre 2013-04-21 08:43:17

回答

4

以下是用PHP的SimpleXML一點OSM立交橋API的例子,我已經編譯,因爲我們沒有在這裏爲PHP和我愛OSM,讓我們來展示一些有用的例子。

第一部分顯示瞭如何使用標準PHP查詢立交橋端點。你不需要的部分,因爲你已經保存在您硬盤中的數據:

<?php 
/** 
* OSM Overpass API with PHP SimpleXML/XPath 
* 
* PHP Version: 5.4 - Can be back-ported to 5.3 by using 5.3 Array-Syntax (not PHP 5.4's square brackets) 
*/ 


// 
// 1.) Query an OSM Overpass API Endpoint 
// 

$query = 'node 
    ["amenity"~".*"] 
    (38.415938460513274,16.06338500976562,39.52205163048525,17.51220703125); 
out;'; 

$context = stream_context_create(['http' => [ 
    'method' => 'POST', 
    'header' => ['Content-Type: application/x-www-form-urlencoded'], 
    'content' => 'data=' . urlencode($query), 
]]); 

# please do not stress this service, this example is for demonstration purposes only. 
$endpoint = 'http://overpass-api.de/api/interpreter'; 
libxml_set_streams_context($context); 
$start = microtime(true); 

$result = simplexml_load_file($endpoint); 
printf("Query returned %2\$d node(s) and took %1\$.5f seconds.\n\n", microtime(true) - $start, count($result->node)); 

對於你的第二個部分是更有趣。這就是查詢你已有的XML數據。這很容易用xpath完成,所用的PHP XML庫基於libxml,它支持XPath 1.0,它很好地覆蓋了各種查詢需求。

以下示例列出了所有學校,並嘗試獲取其名稱。我還沒有介紹的翻譯還沒有,因爲我的樣本數據沒有這些,但是你也可以看看所有類型的名稱,包括翻譯,只是喜歡一個特定的一個):

// 
// 2.) Work with the XML Result 
// 

# get all school nodes with xpath 
$xpath = '//node[tag[@k = "amenity" and @v = "school"]]'; 
$schools = $result->xpath($xpath); 
printf("%d School(s) found:\n", count($schools)); 
foreach ($schools as $index => $school) 
{ 
    # Get the name of the school (if any), again with xpath 
    list($name) = $school->xpath('tag[@k = "name"]/@v') + ['(unnamed)']; 
    printf("#%02d: ID:%' -10s [%s,%s] %s\n", $index, $school['id'], $school['lat'], $school['lon'], $name); 
} 

這裏的關鍵點是XPath的查詢。使用兩個,第一個獲得具有特定標籤的節點。我覺得這是最有趣的一個給你:

//node[tag[@k = "amenity" and @v = "school"]] 

此行說:給我說有一個標籤元素的所有節點元素中它具有ķ屬性值「舒適」v屬性值「學校」。這是你必須過濾那些標記爲設施學校的節點的條件。

而且XPath的再次使用,現在相對於那些學校的節點,看看是否有一個名字,如果是把它牽回家:

tag[@k = "name"]/@v' 

此行說:相對於當前節點,給我v屬性來自標籤元素作爲k屬性值「名稱」。正如你所看到的,一些零件再次與之前的生產線類似。我認爲你們都可以採納他們來滿足你的需求。

因爲不是所有的學校節點有一個名字,默認的字符串是通過將其添加到(當時空的)結果陣列提供用於顯示目的:

list($name) = $school->xpath('tag[@k = "name"]/@v') + ['(unnamed)']; 
                ^^^^^^^^^^^^^^^ 
               Provide Default Value 

所以在這裏我的結果對於代碼 - 例如:

Query returned 907 node(s) and took 1.10735 seconds. 
10 School(s) found: 
#00: ID:332534486 [39.5017565,16.2721899] Scuola Primaria 
#01: ID:1428094278 [39.3320912,16.1862820] (unnamed) 
#02: ID:1822746784 [38.9075566,16.5776597] (unnamed) 
#03: ID:1822755951 [38.9120272,16.5713431] (unnamed) 
#04: ID:1903859699 [38.6830409,16.5522243] Liceo Scientifico Statale A. Guarasci 
#05: ID:2002566438 [39.1347698,16.0736924] (unnamed) 
#06: ID:2056891127 [39.4106679,16.8254844] (unnamed) 
#07: ID:2056892999 [39.4124687,16.8286119] (unnamed) 
#08: ID:2272010226 [39.4481717,16.2894353] SCUOLA DELL'INFANZIA SAN FRANCESCO 
#09: ID:2272017152 [39.4502366,16.2807664] SCUOLA MEDIA 

我希望這已經有用了,如果您有更多的澄清問題,請告訴我。


(由rbwilkinson):這是你如何可以添加額外的參數,以找到其他值。下面的例子中找到一公里範圍內的其他屬性:

$query = 'node 
    ["addr:postcode"~"RM12"] 
    (51.5557914,0.2118915,51.5673083,0.2369398); 
    node 
    (around:1000) 
    ["amenity"~"fast_food"]; 
      out;'; 

$context = stream_context_create(['http' => [ 
    'method' => 'POST', 
    'header' => ['Content-Type: application/x-www-form-urlencoded'], 
    'content' => 'data=' . urlencode($query), 
]]); 

$endpoint = 'http://overpass-api.de/api/interpreter'; 
libxml_set_streams_context($context); 

$result = simplexml_load_file($endpoint); 
printf("Query returned %2\$d node(s) and took %1\$.5f seconds.\n\n", microtime(true) - $start, count($result->node)); 
} 
+0

這是非常棒的答案,這正是我需要的。感謝名單上百萬:) – palAlaa 2013-04-23 07:13:53

+0

aweseome - 我的方式喜歡這個很好的例子 – zero 2014-06-12 12:29:23

+1

- 如果你要收集所有學校 ,想提取立交橋-API學校:注意一些學校包括作爲節點,而另一些地區(多邊形或多面體)或兩者。你需要跟蹤 節點,方式和關係 - 所有類型的對象 CF http://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide#All_kind_of_objects讓所有的 – zero 2014-06-13 15:21:48