1

I am new to PHP's SimpleXML.

update:

The original version of this question was derived from here: OSM Data parsing to get the nodes with child

I am thankful that hakre offered a great example in the comments that makes a overwhelming starting point for my project. Below I have added my own answer to the question, how to refine the code to ad more tags.

I want to filter the data to get the nodes with special category. Here is sample of the OSM data I want to get the whole schools within an area. The first script runs well - but now I want to refine the search and add more tags. I want to store all into MySQL.

So we need to make some XML parsing with PHP:

The following is a little OSM Overpass API example with PHP SimpleXML

<?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
  ["addr:postcode"~"RM12"]
  (51.5557914,0.2118915,51.5673083,0.2369398)->.point;
  (
  node
  (around.point:100000)
  ["amenity"~"school"];
  way
  (around.point:100000)
  ["amenity"~"school"];
  );
  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));

//
// 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);
}

?>

how to get more out of it.. at least the address and the website

should this be added in this part!?

# 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);
}

since i am learning - i break down the code into pieces...

For you the second part is more interesting. That is querying the XML data we have already. This is most easily done with xpath, the used PHP XML library is based on libxml which supports XPath 1.0 which covers the various querying needs very well.

The following example lists all schools and tries to obtain their names as well. I have not covered translations yet because my sample data didn't have those, but you can also look for all kind of names including translations and just prefer a specific one):

//
// 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);
}

The key point here are the xpath queries. Two are used, the first one to get the nodes that have certain tags. I think this is the most interesting one for you:

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

This line says: Give me all node elements that have a tag element inside which has the k attribute value "amenity" and the v attribute value "school". This is the condition you have to filter out those nodes that are tagged with amenity school.

Further on xpath is used again, now relative to those school nodes to see if there is a name and if so to fetch it:

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

This line says: Relative to the current node, give me the v attribute from a tag element that as the k attribute value "name". As you can see, some parts are again similar to the line before. I think you can both adopt them to your needs.

Because not all school nodes have a name, a default string is provided for display purposes by adding it to the (then empty) result array:

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

So here some of the results for that code-example:

Query returned 907 node(s) and took 1.10735 seconds.
more than 2000 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 

and now i try to figure out how i can enter more xpath queries at the above mentioned code

and get out even more important data - see here [url=http://wiki.openstreetmap.org/wiki/Key:contact]Key:contact - OpenStreetMap Wiki[/url]

contact:phone
contact:fax    
contact:website
contact:email

I will dig into all documents and come back later the weekend... and report all the findings

well - i think that i need to extend the xpath requests within the loop where xpath is used again, now relative to those school nodes to see if there is a name and if so to fetch it:

tag[@k = "name"]/@v'
tag[@k = "contact:website"]/@v'
tag[@k = "contact:email"]/@v'

What do you say...?

many many thanks to you for all your help. You are very supportive. Keep up the superb work here....

look forward to any and all help

  • You are already extracting the name. Just run a few more xpath queries inside your loop for all the address keys and the website. – scai Jun 14 '14 at 9:56
  • hello scal - great: many many thanks for the hint. i will try to do so! greetings – zero Jun 14 '14 at 10:38
  • updated the posting and added some more ideas. look forward to some more hints - many thanks – zero Jun 14 '14 at 21:43
  • Don't forget to look for the website key additional to contact:website. And please don't abuse this as a forum. Ask a single, clear, precise question – scai Jun 15 '14 at 8:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.