Technology

Using Virtual Earth Webservices with PHP

Anthony Marshall
Anthony Marshall
14 Jan 2009
blog post featured image
<p><img src="http://tbn3.google.com/images?q=tbn:tiDvIyauUMEubM:http://mis.dost.gov.ph/nrcp/images/php-logo.gif" align="right" /><img src="http://www.popflycreator.ms/content/components/icons/virtualEarthLogo.png" align="right" /> This last week we have been working on a Virtual Earth project with a client who have a large php extranet . As part of the mapping integration we have been helping their developers use the Virtual Earth webservices for Geocoding addresses but there seem to be no up to date code samples for doing this in PHP.</p> <h4>The Soap Options</h4> <p>There are a few options for calling SOAP webservices in PHP, the two most popular seem to be <a href="http://sourceforge.net/projects/nusoap/" target="_blank">NuSoap</a> and the PHP5 built in <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a>. Most of the <a href="http://msdn.microsoft.com/en-us/library/ms980207.aspx" target="_blank">previous PHP examples</a> for calling the older MapPoint web services are using <a href="http://sourceforge.net/projects/nusoap/" target="_blank">NuSoap</a> I believe because of early issues with the built in <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a>, so that is where we started.</p> <h4>NuSoap</h4> <p>We managed to get the GetClientToken method working using <a href="http://sourceforge.net/projects/nusoap/" target="_blank">NuSoap</a> pretty quickly but came across issues when we started trying to call the geocodeservice. Initially we found an issue with the encoding of the SOAP request being rejected by the geocodeservice but after some digging found an option to change it in NuSoap by uncommenting the line &quot;var $soap_defencoding = 'UTF-8';&quot; in nusoap.php.</p> <p>Once we had that working, we were getting errors suggesting we were not sending a Credentials parameter to the GeoCode method, even though we were. We tried lots of different approaches including creating the SOAP envelope by hand and sending it using <a href="http://sourceforge.net/projects/nusoap/" target="_blank">NuSoap</a> but nothing seemed to work. So we started again and this time tried the PHP5 built in <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a>.</p> <h4>PHP5 SoapClient</h4> <p>After converting the code to the PHP5 <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a> we started to have some joy with the GeoCode method and eventually managed to get it to return a result. With a bit of debugging to see the structure of the result we eventually had access to the GeoCodeResult object and its properties!</p> <p>So rather than using two different SOAP clients we now changed our GetClientToken code to use the PHP5 <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a>, but again it didn't exactly go to plan. The PHP5 <a href="http://uk3.php.net/soap" target="_blank">SoapClient</a> does not seem able to download a password protected WSDL, even when using it's documentation credentials options. We tried putting the username/password in the wsdl url but that didn't work either so eventually we had to download a local copy of the wsdl and use that.</p> <h4>The Final Code</h4> <p>The code is split into two parts, the token request:</p> <pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; width: 500px; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #fbfbfb"><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //used to get Virtual Earth webservice token, returns token as string </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> function GetToken($vepUID,$vepPWD){ </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //have to use local copy as cannot get php to send credentials to get it from live. See http://bugs.php.net/bug.php?id=27777 for php bug report </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //remember to update local wsdl when using production </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $vetsWsdl = 'tokenservice.wsdl'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //attempted at sending credentials in url, does work </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //$vetsWsdl = 'http://' . $vepUID . ':' . $vepPWD . '@staging.common.virtualearth.net/find-30/common.asmx?wsdl'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //live url $vetsWsdl = 'http://' . $vepUID . ':' . $vepPWD . '@common.virtualearth.net/find-30/common.asmx?wsdl'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //create soap client, setting username and password used when calling method </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $client = new SoapClient($vetsWsdl, array('login'=&gt;$vepUID,'password'=&gt;$vepPWD,'trace' =&gt; 1));//trace allows us to see last response and request for debugging </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $client_ip = $_SERVER['REMOTE_ADDR'];//wont work for localhost! </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //$client_ip = '86.17.152.241'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //Build the tokenspecification object http://msdn.microsoft.com/en-us/library/cc966768.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $tokenSpecification = array( </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> 'ClientIPAddress' =&gt; $client_ip, </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> 'TokenValidityDurationMinutes' =&gt; 15); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> $getClientToken = array('specification' =&gt; $tokenSpecification); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //call GetClientToken method of token service http://msdn.microsoft.com/en-us/library/cc980876.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $result=$client-&gt;GetClientToken($getClientToken); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //Make sure no fault or error has occurred. </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> if ($client-&gt;fault) </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> { </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> die('Fault occurred using Web Service: '.print_r($res,true)); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> } </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> $veToken = $result-&gt;GetClientTokenResult; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> return $veToken; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> }</pre></pre> <p>and the GeoCode request:</p> <pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; width: 500px; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #fbfbfb"><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //Virtual Earth Platform ID goes here. </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $vepUID = 'YOURUSERID';

</pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //Virtual Earth Platform password goes here. </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $vepPWD = 'YOURPASSWORD';
</pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $veToken = GetToken($vepUID,$vepPWD);//call token function </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //Get geocode </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> $vegWsdl = 'http://staging.dev.virtualearth.net/webservices/v1/metadata/geocodeservice/geocodeservice.wsdl'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //live url $vegWsdl = 'http://dev.virtualearth.net/webservices/v1/metadata/geocodeservice/geocodeservice.wsdl'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //Create soap client </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> $client = new SoapClient($vegWsdl, array('trace' => 1));//trace allows us to see last response and request for debugging </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //create credentials object and fill properties http://msdn.microsoft.com/en-us/library/cc966923.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $credentials = array('Token' => $veToken); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //set geocoding query </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $query = 'SG15 6YF, uk'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //create geocoderequest object and fill properties http://msdn.microsoft.com/en-us/library/cc980924.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $geocodeRequest = array('Credentials' => $credentials,'Query' => $query); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb">
</pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //build geocode methods 'request' parameter </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> $geocode = array('request' => $geocodeRequest);
</pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //call web service method called GeoCode http://msdn.microsoft.com/en-us/library/cc966817.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> $result=$client->Geocode($geocode); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //Make sure no fault or error has occurred. </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> if ($client->fault) </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> { </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> die('Fault occurred using Web Service: '.print_r($res,true)); </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> } </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"></pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //Get GeocodeResponse object from $result http://msdn.microsoft.com/en-us/library/cc980928.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //get first GeocodeResult object as there will be more than one, starting with the most relevant. http://msdn.microsoft.com/en-us/library/cc980950.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> //get the GeocodeLocation object http://msdn.microsoft.com/en-us/library/cc966778.aspx </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //get the Latitude value </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> echo $result->GeocodeResult->Results->GeocodeResult[0]->Locations->GeocodeLocation->Latitude . '<span style="color: #0000ff"><</span><span style="color: #800000">br</span> <span style="color: #0000ff">/></span>'; </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #fbfbfb"> //get the Longitude value </pre><pre style="font-size: 10px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #ffffff"> echo $result->GeocodeResult->Results->GeocodeResult[0]->Locations->GeocodeLocation->Longitude . '<span style="color: #0000ff"><</span><span style="color: #800000">br</span> <span style="color: #0000ff">/></span>';</pre></pre>

<p>&#160;</p> <h4>Final Thoughts</h4> <p>It's been a long time since we have done any real PHP coding and the one thing this exercise has reminded us is just how far .Net has come over the years compared to PHP. What would take minutes to do in .Net has taken hours to figure out and debug in PHP, and with no strongly typed objects it was even more difficult to get at the result objects. But we do not claim to be PHP experts, maybe there are easily and less taxing ways to achieve this, we would <a href="http://www.earthware.co.uk/contact-earthware.aspx" target="_blank">love to hear</a> if you know more!</p>
Close chatbot
Open chatbot
Open chatbot