• Posts Tagged ‘hack’

    TTMMHTM: Braille body mods, Tesco hack, Placemaker talk video, old superheroes, steampunk, accessible Opera and cat control

    Tuesday, July 14th, 2009

    Things that made me happy this morning:

    Geo this! Geolocate WordPress posts with Greasemonkey and Yahoo Placemaker

    Monday, June 22nd, 2009

    Geolocating content on the web is a great idea. By embedding latitude and longitude and real place names in your document you allow data mining for location or easy display on a map.

    The problem up to now was that it is quite a job to find out the correct geo information from a text or a document and it is quite a pain to enter the information by hand.

    Yahoo Placemaker is a web service that helps you with that – you give it some text or a document URL and it returns you all the things it found in there that resemble a geographical location back. The issue with doing that on a live site is that you slow down your site immensely as you need to look up every time.

    The more logical place to do the lookup with Placemaker is when you edit your document. I thought this would be cool to have for this WordPress install here and wrote a small GreaseMonkey script that injects a new “Geo this!” button in the main WP form:

    Geo this - button by  you.

    When I hit the button the script does an Ajax request using the Placemaker open YQL table to get the information for the currently edited text.

    Once it found the information it adds it at the end of the document as a GEO microformat. Each found entry starts with a comment that tells you what Placemaker matched and considered a geographical location. As it is not infallible this makes it easy for you to delete wrong entries.

    Geo this - added microformats by  you.

    Try it out yourself:

    This is pretty much rough and ready and I’d be happy for feedback how to improve it.

    Postcode from latitude and longitude or even IP – fun with Geo APIs and YQL

    Tuesday, June 9th, 2009

    One of the more complex things about GeoFill was to get postcode information from an IP. However with a collection of APIs and a collated YQL statement even this was possible.

    The first thing I needed to get was the IP of the user. This is done with the GeoIP API based on the GeoLite API from MaxMind. This is available as an open table in YQL and can be used thus:

    select * from ip.location where ip=""

    Try the lookup in the console or check the lookup result

    Response": {
    "Ip": "216.39.58.17",
    "Status": "OK",
    "CountryCode": "US",
    "CountryName": "United States",
    "RegionCode": "06",
    "RegionName": "California",
    "City": "Sunnyvale",
    "ZipPostalCode": "94089",
    "Latitude": "37.4249",
    "Longitude": "-122.007",
    "Gmtoffset": "-8.0",
    "Dstoffset": "-7.0"
    }

    This gives us a lot of information. What’s really important here is latitude and longitude, as this can be used in the flickr.places API to get a where on earth ID which is a much more defined identifier:

    select * from flickr.places where (lat,lon) in (
    select Latitude,Longitude from ip.location where ip=""
    )

    Try the flickr places call in the console or check the flickr result

    "places": {
    "accuracy": "16",
    "latitude": "37.4249",
    "longitude": "-122.007",
    "total": "1",
    "place": {
    "latitude": "37.371",
    "longitude": "-122.038",
    "name": "Sunnyvale, California, United States",
    "place_id": "P_ls_fybBJwdHP8t",
    "place_type": "locality",
    "place_type_id": "7",
    "place_url": "/United+States/California/Sunnyvale",
    "timezone": "America/Los_Angeles",
    "woeid": "2502265"
    }
    }
    Here the interesting part is the woeid which we can use to dig deeper into geo.places:
    select * from geo.places where woeid in (
    select place.woeid from flickr.places where (lat,lon) in (
    select Latitude,Longitude from ip.location where ip=""
    )
    )

    Try the geo places call in the console or check the geo places result

    The result is all the information you’d ever want.

    "place": {
    "lang": "en-US",
    "xmlns": "http://where.yahooapis.com/v1/schema.rng",
    "yahoo": "http://www.yahooapis.com/v1/base.rng",
    "uri": "http://where.yahooapis.com/v1/place/28751237",
    "woeid": "28751237",
    "placeTypeName": {
    "code": "22",
    "content": "Suburb"
    },
    "name": "Fairgrounds",
    "country": {
    "code": "US",
    "type": "Country",
    "content": "United States"
    },
    "admin1": {
    "code": "US-CA",
    "type": "State",
    "content": "California"
    },
    "admin2": {
    "code": "",
    "type": "County",
    "content": "Santa Clara"
    },
    "admin3": null,
    "locality1": {
    "type": "Town",
    "content": "San Jose"
    },
    "locality2": {
    "type": "Suburb",
    "content": "Fairgrounds"
    },
    "postal": {
    "type": "Zip Code",
    "content": "95112"
    },
    "centroid": {
    "latitude": "37.326611",
    "longitude": "-121.878441"
    },
    "boundingBox": {
    "southWest": {
    "latitude": "37.275379",
    "longitude": "-121.89254"
    },
    "northEast": {
    "latitude": "37.330879",
    "longitude": "-121.808723"
    }
    }
    }

    Newsmap – using Placemaker to add geo location to a news feed

    Friday, May 22nd, 2009

    I am right now very excited about the new Placemaker beta – a location extraction web service released at Where2.0. Using Placemaker you can find all the geographical locations in a feed or a text or a web url and you get them back as an array of places.

    As a demo I took the Yahoo News feed and ran it through Placemaker. The resulting places are plotted on a map and the map moves from location to location when you hover over the news items.

    The result is online at http://isithackday.com/hacks/placemaker/map.php

    Yahoo News Map by  you.

    Getting the data from the data feed and running it through placemaker is very straight forward. I explained the basic principle in this blog post on the Yahoo Developer Network blog. The only thing to think about is to define the input and output types correctly:

    
    <?php
    $key = 'PASTE YOUR API KEY HERE';
    $apiendpoint = 'http://wherein.yahooapis.com/v1/document';
    $url = 'http://rss.news.yahoo.com/rss/topstories';
    $inputType = 'text/rss';
    $outputType = 'rss';
    $post = 'appid='.$key.'&documentURL='.$url.
    '&documentType='.$inputType.'&outputType='.$outputType;
    $ch = curl_init($apiendpoint);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $results = curl_exec($ch);
    ?>
    

    If you look at the source of this example you will find that Placemaker injected contentlocation elements in the feed itself:

    
    <cl:contentlocation
    xmlns:georss="http://www.georss.org/georss" xmlns:cl="http://wherein.yahooapis.com/v1/cle"
    xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:lang="en">
    <cl:place>
    <cl:woeId>2514815</cl:woeId>
    <cl:name><![CDATA[Washington, DC, US]]></cl:name>
    <cl:latitude>38.8913</cl:latitude>
    <cl:longitude>-77.0337</cl:longitude>
    </cl:place>
    <cl:place>
    <cl:woeId>23424793</cl:woeId>
    <cl:name><![CDATA[Cuba]]></cl:name>
    <cl:latitude>21.511</cl:latitude>
    <cl:longitude>-77.8068</cl:longitude>
    </cl:place>
    <cl:place>
    <cl:woeId>23424977</cl:woeId>
    <cl:name><![CDATA[United States]]></cl:name>
    <cl:latitude>48.8907</cl:latitude>
    <cl:longitude>-116.982</cl:longitude>
    </cl:place>
    <cl:place>
    <cl:woeId>55843872</cl:woeId>
    <cl:name><![CDATA[Guantanamo Bay, Caimanera,
    Guantanamo, CU]]></cl:name>
    <cl:latitude>19.9445</cl:latitude>
    <cl:longitude>-75.1541</cl:longitude>
    </cl:place>
    </cl:contentlocation>
    

    You’ll also notice that the elements are namespaced and the names of the locations in CDATA blocks, both things I hate with a passion. Not because they don’t make sense, but because simplexml can be drag to make understand them.

    What I wanted to do with this data was twofold: create a JSON array of geo locations to plot on a map and a display of the news content. This is the PHP that does that:

    
    $places = simplexml_load_string($results, 'SimpleXMLElement',
    LIBXML_NOCDATA);
    // if there are elements found
    if($places->channel->item){
    // start a JSON array
    $output .= '[';
    // start the HTML output
    $html = '<ul id="news">';
    // set the counter - this will be needed to link news
    // items and map markers
    $count = 0;
    // loop over RSS items
    foreach($places->channel->item as $p){
    // set inner counter (as there are more locations per news item)
    $innercount = 0;
    // start the HTML list item and give it an ID with the counter
    // value
    $html .=  '<li id="news'.$count.'"';
    // all child elements with the defined namespace
    $locs = $p->children('http://wherein.yahooapis.com/v1/cle');
    // check that there is a location sub-element in this item
    if($locs->contentlocation){
    // if there is one, add a class to the LI
    $html .= ' class="haslocation"';
    // start an array for displaying of the locations under the
    // news items
    $dlocs = array();
    // loop over all the places found for this item
    foreach($locs->contentlocation->place as $pl){
    // append a new JS object with the location data
    // and a unique ID to the locations array
    $locations[] = '{<a href=""'.$pl->name.'","title" title="">name</a>":"'.
    preg_replace('/n+/','',addslashes($p->title)).
    '",<a href=""'.$pl->latitude" title="">lat</a>.
    '",<a href=""'.$pl->longitude.'","id":"m" title="">lon</a>'.
    $count.'x'.$innercount.'"}';
    // add the location name to the display locations array
    $dlocs[] = $pl->name;
    // increase the inner count to ensure that every marker has
    // a unique ID
    $innercount++;
    }
    }
    // append the HTML for the news item
    $html.='><h2><a href="'.$p->link.'">'.$p->title.'</a></h2><p>'.
    $p->description.'</p>';
    // if locations were found, add them
    if(sizeof($dlocs)>0){
    $html.='<p class="locations">Locations: '.join(',',$dlocs).'</p>';
    }
    // end the list item
    $html.='</li>';
    // increase the counter
    $count++;
    }
    // join the json object data with a comma and close the JSON array
    $output .= join(',',$locations);
    $output .= ']';
    // if there are no items simply return nothing
    } else {
    $output = '';
    }
    // and this ends the HTML
    $html.= '</ul>';
    ?>
    

    The result of this can be seen here http://isithackday.com/hacks/placemaker/map-2.php.

    The JavaScript to show the map is pretty straight forward and more or less the demo example of the maps API:

    
    // will be called with the array assembled in PHP
    function placeonmap(o){
    // if there are locations
    if(o.length > 0){
    // create a new geopoints array to hold all locations
    // this is needed to determine the original zoom
    // level of the map
    var geopoints = [];
    // add map with controls
    var map = new YMap(document.getElementById('map'));
    map.addZoomLong();
    map.addPanControl();
    // loop over locations
    for(var i=0;i<o.length;i++){
    // define a new geopoint and store it in the array
    var point = new YGeoPoint(o[i].lat,o[i].lon);
    geopoints.push(point);
    // create a new marker and give it the unique
    // id defined in the PHP. Pop up the title of
    // the news item and the name of the location when the
    // user hovers over the marker
    var newMarker = new YMarker(point,o[i].id);
    newMarker.addAutoExpand(o[i].title + '('+o[i].name+')');
    map.addOverlay(newMarker);
    }
    // define best zoom level and show map
    var zac = map.getBestZoomAndCenter(geopoints);
    map.drawZoomAndCenter(zac.YGeoPoint,zac.zoomLevel);
    }
    // add a mouseover handler to the list of results
    YAHOO.util.Event.on('news','mouseover',function(e){
    // remove the "news" text of the ID of the current target
    // as we named the list items news0 to news19
    var id = YAHOO.util.Event.getTarget(e).id.replace('news','');
    // if there is still something left we have one of the news
    // items
    if(id!==''){
    // get the first marker with the ID we defined in the loop.
    var marker = map.getMarkerObject('m'+id+'x0');
    // if there is one, pan the map there and show the message
    // attached to it.
    if(marker){
    map.panToLatLon(marker.YGeoPoint);
    marker.openAutoExpand();
    }
    }
    });
    }
    // call placeonmap with the JSON array
    placeonmap(<?php echo $output;?>);
    

    That’s pretty much it. I am sure it can be refined, but it is amazing how easy it is to get geo information into any text with Placemaker.

    TTMMHTM: Sorry Jinho answers, twittering cat flaps, hot goths and the real AYB

    Monday, May 18th, 2009

    Things that made me happy this morning: