Find the answer to your Linux question:
Results 1 to 7 of 7
I am new to PHP's SimpleXML. The original version of this question was derived from here: OSM Data parsing to get the nodes with child I am thankful that hakre ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Linux User
    Join Date
    May 2013
    Posts
    258

    php simpleXML :parsing an osm-file


    I am new to PHP's SimpleXML.

    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 SimpleXM


    Code:
    <?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 adress and the website

    where to add those tags in the request`?

    in this line:
    Code:
     list($name) = $school->xpath('tag[@k = "name"]/@v') + ['(unnamed)'];
    many thanks for any and all help!!
    Last edited by sayhello; 06-14-2014 at 09:33 AM.
    Akoya P 6512 15" OpenSuse 13.1: AMD Athlon X2 P320
    Samsunng q 210, 12,1" OpenSuse 13.1: Intel® Core™ 2 Duo Proz. P8400 2,26 GHz 1066 MHz FSB 3 MB

  2. #2
    Linux Guru
    Join Date
    Dec 2013
    Location
    Victoria, B.C. Canada
    Posts
    1,659

  3. #3
    Linux User
    Join Date
    May 2013
    Posts
    258
    hello dear gregm, - many thanks

    we already extracting the name.

    well i am not too familiar with PHP.

    We just should run a few more xpath queries inside the loop for all the address keys and the website.


    we need to do this here
    Code:
    # 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);
    }

    i just have had a quick view on the above mentioned site. i try to figure out how to do this - for any and all hints i am thankful

    greetings
    Akoya P 6512 15" OpenSuse 13.1: AMD Athlon X2 P320
    Samsunng q 210, 12,1" OpenSuse 13.1: Intel® Core™ 2 Duo Proz. P8400 2,26 GHz 1066 MHz FSB 3 MB

  4. $spacer_open
    $spacer_close
  5. #4
    Linux Guru
    Join Date
    Dec 2013
    Location
    Victoria, B.C. Canada
    Posts
    1,659
    The link gave a description of the API you're using. If you wish to program in PHP you need to understand at least a minimum of the language and that is where you should start. You should read up on xml and xpath and try to understand how the API exposes the data.

    A useful tool in PHP is print_r - it allows you to examine objects you're working with and understand them better. Here's some more links:
    XML Tutorial
    XPath Syntax
    https://php.net/manual/en/function.s...-load-file.php
    Study the object returned by simplexml_load_file.

    If you program by throwing crap at the wall and seeing what sticks you wind up with a crap covered wall.

  6. #5
    Linux User
    Join Date
    May 2013
    Posts
    258
    hello dear gregm

    many many thanks for the quick reply great to hear from you



    One starting point can be: Start by examining the entire XML object.
    Seeing what nodes exist and what they are called makes it much easier to create the search string.

    Code:
        echo "<pre>";
        var_dump($result);
        echo "</pre>";
    well i am learing - and this code snippets make me learn alot.

    The following code 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):

    Code:
    //
    // 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.

    In the above mentioend example two are used, the first xpath queriy is to get the nodes that have certain tags.I think this is the most interesting one for me:

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

    This line says: Give us 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 we 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:

    Code:
    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:

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

    So here my results for that code-example:
    Query returned xxxxx node(s) and took 1.10735 seconds.
    2179 School(s) found: the last ones are shown below.....

    #2151: ID:2688357765 [51.4668941,-0.9731135] New Directrions, North Reading
    #2152: ID:2702504696 [51.5884265,-0.7829013] Burford School
    #2153: ID:2702549737 [51.5802201,-0.7653918] Great Marlow School
    #2154: ID:2706219304 [51.3779317,-0.0895302] ARK Oval Primary Academy
    #2155: ID:2706219314 [51.3871935,-0.0623001] Ashburton Primary School
    #2156: ID:2706219320 [51.3210977,-0.1398859] CALAT Smitham Centre
    #2157: ID:2706219326 [51.3638861,-0.0922032] Elmhurst School
    #2158: ID:2706219339 [51.4007121,-0.0743710] Harris Academy South Norwood
    #2159: ID:2706219343 [51.3831662,-0.0405476] Orchard Way Primary School
    #2160: ID:2706219347 [51.3531047,-0.0959447] Purley Oaks Primary School
    #2161: ID:2706219348 [51.3428384,-0.0069931] Rowdown Primary School
    #2162: ID:2706219350 [51.3954917,-0.0732185] South Norwood Primary School
    #2163: ID:2706219351 [51.3377151,-0.1230482] St David's Preparatory School
    #2164: ID:2706219353 [51.3993760,-0.1144352] Winterbourne School
    #2165: ID:2717394621 [51.8706538,0.1480886] Prep
    #2166: ID:2717394636 [51.8685838,0.1463720] Pre-Prep
    #2167: ID:2722704201 [51.1398429,-0.0457445] Felbridge Primary School
    #2168: ID:2723815070 [50.8465429,-0.3030261] Lancing College
    #2169: ID:2727170814 [51.5780664,-0.0249051] Adult Education Centre
    #2170: ID:2833253896 [50.9928140,-0.7774996] (unnamed)
    #2171: ID:2837001831 [51.1783749,-0.7970866] More House School
    #2172: ID:2865091022 [50.9090614,-0.5565425] Dorset House School
    #2173: ID:2882477853 [51.6261198,-0.7349665] Bowerdean Primary School
    #2174: ID:2901434856 [51.6542477,-0.3098923] The Haberdashers' Aske's Girls School
    #2175: ID:2901434857 [51.6565707,-0.3129822] The Haberdashers' Aske's Boys School


    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 Key:contact - OpenStreetMap Wiki

    contacthone
    contact:fax
    contact:website
    contact:email
    i will digg 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:


    Code:
    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....



    update_



    this may be helpful if we need more insights into the k tags and the values: Parsing and Analyzing OpenStreetMap Export Files

    http://devio.wordpress.com/2012/09/0...-export-files/


    OpenStreetMap provides geo-coded information in the form of extracted .osm files for download.
    .osm files are XML files with a defined schema, but this information alone does not really help
    in analyzing the files. The reason is that the simple structure <node><tag /></node> distinguishes
    its data by the tag’s k attribute value:

    Code:
      <node id="567053682" [...] lat="48.2066502" lon="16.3707086">
        <tag k="addr:postcode" v="1010"/>
        <tag k="addr:country" v="AT"/>
        <tag k="addr:street" v="Neuer Markt"/>
        <tag k="addr:city" v="Wien"/>
        <tag k="addr:housenumber" v="16"/>
      </node>

    second update:


    Well to extend the xpath-requests i tried the following


    this method:


    Code:
        list($website) = $school->xpath ('tag[@k = "website"]/@v'):
        list($email) = $school->xpath ('tag[@k = "email"]/@v');

    and later on that method:

    Code:
     list($name) = $school->xpath ('tag[@k = "name"]/@v','tag[@k = "website"]/@v','tag[@k = "email"]/@v') + ['(unnamed)'];

    well both ways were wrong - since i was not able to get better resluts:


    Code:
     
    2 School(s) found:
    PHP Warning:  SimpleXMLElement::xpath() expects exactly 1 parameter, 3 given in /home/martin/php/osm6.php on line 57
    PHP Fatal error:  Unsupported operand types in /home/martin/php/osm6.php on line 57
    martin@linux-70ce:~/php>
    well i see that my trials to write correct xpath-queries are not 100% correct

    i have to rework all according this documentation
    http://de1.php.net/manual/en/simplexmlelement.xpath.php

    if anybody can help out i would be happy

    greetings
    Last edited by sayhello; 06-15-2014 at 04:55 PM.
    Akoya P 6512 15" OpenSuse 13.1: AMD Athlon X2 P320
    Samsunng q 210, 12,1" OpenSuse 13.1: Intel® Core™ 2 Duo Proz. P8400 2,26 GHz 1066 MHz FSB 3 MB

  7. #6
    Linux User
    Join Date
    May 2013
    Posts
    258
    well the issue is the following:

    the question is : how-to-select-all-nodes-of-a-domdocument-with-a-single-domxpath-expression?

    What is the xpath expression to select all nodes of a document?



    I tried
    Code:
    with //node():
    $xpath->query('//node()');

    which returns all element nodes only
    (I assume because of //).
    Is there a way to add other nodes
    like attributes and textnodes in attribute values?

    well i want to catch all the nodes within the document.

    cf: documentation. -
    php - How to select all nodes of a DOMDocument with a single DOMXpath expression? - Stack Overflow
    Akoya P 6512 15" OpenSuse 13.1: AMD Athlon X2 P320
    Samsunng q 210, 12,1" OpenSuse 13.1: Intel® Core™ 2 Duo Proz. P8400 2,26 GHz 1066 MHz FSB 3 MB

  8. #7
    Linux Guru
    Join Date
    Dec 2013
    Location
    Victoria, B.C. Canada
    Posts
    1,659
    "simplexml_load_file" loads the entire document into a resource of the type SimpleXMLElement. A SimpleXMLElement is Traversable therefore it can be accessed with foreach. It also has a number of methods to allow access - one of with is xpath. This takes an xpath query as its argument. In the documentation it show exactly one argument so your error message is very understandable. If your goal i to load the entire document that's all it takes.It also has methods to add elements.

    I believe You are going at this wrong. Reading other peoples code is an important skill, however, taking that code without understanding it and trying to hack together a working program is seldom a good idea.

    Start with this:
    Code:
    $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);
    
    print_r (result);
    Spend some time examining what is printed out.

    An even better approach is to start with a very simple XML file and learn to manipulate and examine it, then come back to the more complicated example and write your own code!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •