2011-01-23

2011-01-20

(Desktop) Safari & (W3C) Geolocation API

I don't know why this thing fails in desktop Safari. I'm using Modernizr (which is bundled with the highly recommended HTML5 Boilerplate) which reports that the Geolocation API is supported but I'm unable to even get any dialog asking the user for permission to geolocate (which is, if I read the draft correctly, REQUIRED to be implemented).

Also, it seems that others (quite a few people actually) have had this same problem but I have been unable to find a solution yet. Below is a snippet from the code; in desktop Safari the PositionErrorCallback (the latter cb function) gets called after timeout but no luck with PositionCallback, no matter how long the timeout value is. Other tested browsers work as expected.

Referenced in other places:

(Note to self: check if Google Gears, which is still installed, is causing this?)

var position = null;

$(document).ready(function() {

    if(!Modernizr.geolocation) {
        return;
    }

    navigator.geolocation.watchPosition(
        function(pos) {
            position = {};
            position.lat = pos.coords.latitude;
            position.lng = pos.coords.longitude;
            position.allowed = true;
            init();
        },
        function(error) {
            position = {};
            position.allowed = false;
        },
        {
            enableHighAccuracy: false,
            timeout: 10000,
            maximumAge: 86400000
        }
    );

    checkposition();
});

function checkposition()
{
    log("checkposition()");
    if(!position) {
        setTimeout(function() {
            checkposition();
        }, 1000);
        return;
    } else {
        if(position.allowed) {
            log("checkposition(): got position: " + position.lat + "," + position.lng);
            fetchephemeris();
        } else {
            log("checkposition(): could not get a position, giving up");
            $("#geolocate").hide();
        }
    }    
}

2011-01-10

QUERY_STRING parsing in plain C

As far as I can tell (which, I'll be the first one to admit, doesn't count for that much) this code is so simple that there are no holes that could be exploited.

  char * query = getenv("QUERY_STRING");
  char * pair;
  char * key;
  double value;
 
  if(query && strlen(query) > 0) {
    pair = strtok(query, "&");
    while(pair) {
      key = (char *)malloc(strlen(pair)+1);
      sscanf(pair, "%[^=]=%lf", key, &value);
      if(!strcmp(key, "lat")) {
        lat = value;
      } else if(!strcmp(key, "lng")) {
        lng = value;
      }
      free(key);
      pair = strtok((char *)0, "&");
    }
  }

2010-11-21

jQuery Boids (Plugin)

From the README:

"My first attempt making a jQuery plugin, following the guidelines at: http://docs.jquery.com/Plugins/Authoring

Boids code adapted from Javascript Boids by Ben Dowling, see: http://www.coderholic.com/javascript-boids/

If this is bound to the window resize event, then the jQuery resize event plugin by "Cowboy" Ben Alman should be used as it throttles the window resize events. See: http://benalman.com/projects/jquery-resize-plugin/"

The plugin uses HTML Canvas to render the Boids, so a modern browser with Canvas support is required for this to work. I tested with Chrome, Safari and Firefox. IE with Excanvas was painfully slow…

Code is hosted at GitHub: https://github.com/kahara/jQuery-Boids

Demo is at: http://jonikahara.com/lab/jQuery-Boids/test.html

2010-07-01

Solar Impulse

«If an aircraft is able to fly day and night without fuel, propelled only by solar energy, let no one claim that it is impossible to do the same thing for motor vehicles, heating and air conditioning systems and computers. This project voices our conviction that a pioneering spirit with political vision can together change society and bring about an end to fossil fuel dependency.»

Bertrand Piccard

2010-04-03

Arduino, DS18B20, Ethernet Shield, Pachube.Com

Kotkansaari Sensorium

Update 2: The sensor is now outside, and running on parasitic power.

Update: The data is now available in a more mobile-friendly web page here.

Arduino code, based on (i.e. copypasted & modified a little) stuff from http://www.dial911anddie.com/weblog/2009/12/arduino-ethershield-1wire-temperature-sensor-pachube/ is below. Requires the 1-wire library and the Dallas Temperature Control library, both of which can be downloaded from here. Original code utilized DHCP, but I found this to be somewhat unstable and went with a static IP address instead.

DS18B20 gets it's power from Arduino, and the data line (that's the center pin) is connected to Arduino pin 8. Data line is pulled up to +5V through a 4k7 resistor, as suggested in Maxim literature. Parasitic power supply was not used as proper voltage was readily available from Arduino. Please note that even though parasitic power is not used, the pull-up resistor is still necessary (see the data sheet).

#include <Ethernet.h>
#include <OneWire.h>
#include <DallasTemperature.h>

char PACHUBE_API_STRING[] = "";  // Your API key
int PACHUBE_FEED_ID = 0; // Your feed ID 

// Digital IO port used for one wire interface
int ONE_WIRE_BUS = 8 ;

// Ethernet mac address - this needs to be unique
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// IP addres of www.pachube.com
byte server[] = { 209,40,205,190 };

// Arduino address
byte ip[] = { 10, 0, 0, 223 };
byte gateway[] = { 10, 0, 0, 2 };

char version[] = "PachubeClient Ver 0.01c";

#define CRLF "\r\n"

// simple web client to connect to Pachube.com 
Client client(server, 80);

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// 1wire device address
DeviceAddress thermometer;

void setup()
{
   // Note: Ethernet shield uses digitial IO pins 10,11,12, and 13   
   Serial.begin(9600);
  
   Serial.println(version);
   Serial.println();
  
   // locate devices on the 1Wire bus
   Serial.print("Locating devices on 1Wire bus...");
   sensors.begin();
   int count = sensors.getDeviceCount();
   Serial.print("Found ");
   Serial.print( count );
   Serial.println(" devices on 1wire bus");

   // select the first sensor   
   for ( int i=0; i<count; i++ )
   {
      if ( sensors.getAddress(thermometer, i) ) 
      {
         Serial.print("1wire device ");
         Serial.print(i);
         Serial.print(" has address: ");
         printAddress(thermometer);
         Serial.println();
      }
      else
      {
         Serial.print("Unable to find address for 1wire device "); 
         Serial.println( i );
      }  
   }
  
   // show the addresses we found on the bus
   Serial.print("Using 1wire device: ");
   printAddress(thermometer);
   Serial.println();

   // set the resolution to 9 bit 
   sensors.setResolution(thermometer, 9);

   Serial.print("Initializing ethernet...");  
   delay(5000);
   Ethernet.begin(mac, ip, gateway);
   delay(5000);
   Serial.println(" done.");
}

void sendData()
{     
   float temp = sensors.getTempC(thermometer);
   //float temp = sensors.getTempF(thermometer);
   Serial.print("Temp=");
   Serial.println(temp);
  
   Serial.println("connecting...");

   if (client.connect()) 
   {
      Serial.println("connected");
      
      client.print(
         "PUT /api/feeds/" );
      client.print(PACHUBE_FEED_ID);
      client.print(".csv HTTP/1.1" CRLF
                   "User-Agent: Fluffy Arduino Ver 0.01" CRLF
                   "Host: www.pachube.com" CRLF 
                   "Accept: */" "*" CRLF  // need to fix this 
                   "X-PachubeApiKey: " );
      client.print(PACHUBE_API_STRING);
      client.print( CRLF 
                    "Content-Length: 5" CRLF
                    "Content-Type: application/x-www-form-urlencoded" CRLF
                    CRLF );
      client.println(temp);
      unsigned long reqTime = millis();
      
      // wait for a response and disconnect 
      while ( millis() < reqTime + 10000) // wait 10 seconds for response  
      {
         if (client.available()) 
         {
            char c = client.read();
            Serial.print(c);
         }

         if (!client.connected()) 
         {
            Serial.println();
            Serial.println("server disconnected");
            break;
         }
      }
      
      Serial.println("client disconnecting");
      Serial.println("");
      client.stop();
   } 
   else 
   {
      Serial.println("connection failed");
   }
}

void printAddress(DeviceAddress deviceAddress)
{
   for (uint8_t i = 0; i < 8; i++)
   {
      if (deviceAddress[i] < 16) Serial.print("0");
      Serial.print(deviceAddress[i], HEX);
   }
}

void loop()
{
   sensors.requestTemperatures(); // Send the command to get temperatures
   sendData();
   delay( ( 5l * 60l * 1000l) - 11000l  ); // wait 5 minutes
}

2009-10-11

Feeding lm_sensors Data to a Remote Database

A more correct way to do this would be to use libsensors(3) but I couldn't find Python bindings for it, so I just read and parse the output of the sensors(1) program. The temperature and fanspeed readings come from a spare Asus Eee PC and the raw data can be seen here (may be offline). I wish I had a more varying and interesting data source but this will do for now for the purpose of playing with Google Chart API.


#!/usr/bin/env python

import os, scanf

temperature = 0
fanspeed = 0

for line in os.popen('/usr/bin/sensors'):
    if len(line) < 2:
        continue

    fields = line.split()

# Python scanf from http://hkn.eecs.berkeley.edu/~dyoo/python/scanf/

    key = fields[0]
    if key == 'temp1:':
        temperature = scanf.sscanf(fields[1], '+%d.0')
    if key == 'fan1:':
        fanspeed = scanf.sscanf(fields[1], '%d')

import urllib2

# Authentication code ripped from python.org docs

password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
top_level_url = "http://jonikahara.com/lab/thermometer/update"
password_mgr.add_password(None, top_level_url, 'USERNAME', 'PASSWORD')
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
opener = urllib2.build_opener(handler)
opener.open('http://jonikahara.com/lab/thermometer/update?' + 'temperature=' + str(temperature[0]) + '&fanspeed=' + str(fanspeed[0]))

Sunday, Kauppakatu (Photo)

Sunday, Kauppakatu

2009-09-26

Site Updates

I did some cleaning up on jonikahara.com and removed useless stuff. Now there's only the blog page and the contact info page. The blog part shows the latest post in full. A list of earlier posts can be revealed by pressing the "Archive" button below that. In the archive listing, a given post can be revealed by clicking its title; post content will be loaded inline with a little help from jQuery $.ajax(). Check out the script if you're into that kind of stuff. (Note: there was a bug in jQuery 1.3.2 that made IE fail when using the :contains() selector – a patch was necessary.)

I also changed my DOCTYPE to HTML5.

And because I got a Typekit invitation (page me if you want one) and as their introductory price of $24.99 per year for personal use seemed quite reasonable I decided to check it out. The thing worked like a charm, at least after I had sorted out some funky bits in my CSS. I'm using Coolvetica (Larabie) in the "JONIKAHARA.COM" part at the top, DDT (Typodermic) in post titles and Legendum (Rogier van Dalen) for the rest. Typekit requires @font-face to work, so you won't be seeing these fancy fonts if your browser does not support it.

2009-09-22

LED Display Hacking (Part 3)

First, here's a more readable view of one seventh of the circuit between the demultiplexer and the LED array, this time with an additional current-limiting resistor between the transistors:

LED Driver Circuit

Speaking of current, the whole circuit draws quite a bit of it, and it may be too much for a single 7805 to handle. Proper heat sink attached to the regulator is at least required, or a different power supply arrangement if that fails to provide enough cooling.

I got some of the LEDs to actually light up, but the addressing appears not to be as straightforward as I had naïvely assumed. More tinkering needed.

2009-09-06

LED Display Hacking (Part 2)

Did some "reverse engineering" on the driver part of the device introduced in a recent post and "designed" (ahem…) a new driver circuit based on my findings. See some amazingly craptastic ASCII art below.

Q1..Q7   BC327
Q8..Q14  TIP32C
IC1      74LS138
R1..R7   220 OHMS

                                       IC1
           ·-------·               ·---------·   
           !       !               !         !
           ! Q8    ! Q1            !         !
           b       e               !         !
ARRAY 0 --c e-·     b-----|R1|------ O0  Vcc ----- +5V
              !    c               !(15) (16)!
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q9    ! Q2            !         !
           b       e               !         !
ARRAY 1 --c e-·     b-----|R2|------ O1  GND ----- GND
              !    c               !(14) (8) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q10   ! Q3            !         !
           b       e               !         !
ARRAY 2 --c e-·     b-----|R3|------ O2  E1  ----- GND
              !    c               !(13) (4) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q11   ! Q4            !         !
           b       e               !         !
ARRAY 3 --c e-·     b-----|R4|------ O3  E2  ----- GND
              !    c               !(12) (5) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q12   ! Q5            !         !
           b       e               !         !
ARRAY 4 --c e-·     b-----|R5|------ O4  E3  ----- ARDUINO PIN 2
              !    c               !(11) (6) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q13   ! Q6            !         !
           b       e               !         !
ARRAY 5 --c e-·     b-----|R6|------ O5  A0  ----- ARDUINO PIN 5
              !    c               !(10) (1) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
           ·-------·               !         !
           !       !               !         !
           ! Q14   ! Q7            !         !
           b       e               !         !
ARRAY 6 --c e-·     b-----|R7|------ O6  A1  ----- ARDUINO PIN 6
              !    c               !(9)  (2) !
              !    !               !         !
             +5V  GND              !         !
                                   !         !
                                   !         !
                                   !         !
                                   !         !
                                   !     A2  ----- ARDUINO PIN 7
                                   !     (3) !
                                   !         !
                                   !         !
                                   !         !
                                   ··--------·

   DATA ------------------------------------------ ARDUINO PIN 4

  CLOCK ------------------------------------------ ARDUINO PIN 3

On the right side is the part that connects to Arduino: pins 5…7 select which one of the LED arrays will be lit and pin 2 enables the 74138 output. Arduino pin 4 feeds data to shift registers in the display part; pin 3 clocks them. (Note to self: check that clock part; how much current will 74164's draw?)

Note to self: add resistors between BC327 emitter and TIP32C base.

74138 truth table is as follows:

 I N P U T S     O U T P U T S
------+-------+----------------
E E E ! A A A ! O O O O O O O O
1 2 3 ! 0 1 2 ! 0 1 2 3 4 5 6 7
- - - + - - - + - - - - - - - -
H X X ! X X X ! H H H H H H H H 
X H X ! X X X ! H H H H H H H H 
X X L ! X X X ! H H H H H H H H 
L L H ! L L L ! L H H H H H H H 
L L H ! H L L ! H L H H H H H H 
L L H ! L H L ! H H L H H H H H 
L L H ! H H L ! H H H L H H H H 
L L H ! L L H ! H H H H L H H H 
L L H ! H L H ! H H H H H L H H 
L L H ! L H H ! H H H H H H L H 
L L H ! H H H ! H H H H H H H L 

As can be seen the 74138 outputs (pins 9…15) are active-low. The outputs drive BC327 (PNP) transistors, which then drive TIP32C (PNP) transistors. TIP32C's drive the display modules.

2009-07-23

Shadowbox.js (It Works)

At last, a Lightbox script that actually seems to work mostly as advertised. The author requires commercial customers to pay for the script, but the fee is more than reasonable. It works with many Javascript libraries (jQuery, MooTools, Prototype, …).

You can use the usual attributes to group images together (rel), give images a caption (title) et cetera.

The only thing I had to figure out to make the script work was that it uses document.write() to load its' dependencies, so you can't initialize Shadowbox in jQuery's $(document).ready() (at least not easily). The initialization script must be put in HTML document body, as in:

<script type="text/javascript"> 
Shadowbox.init();
</script>

See my simple test of Shadowbox.

2009-05-26

Note to Self (Streamer/FFmpeg time lapse movie creation)

Grab nine hundred frames, one frame per second:

streamer -t 900 -i composite1 -s 640x480 -r 1 -o frame-00000.jpeg

Encode a time lapse movie from those frames:

ffmpeg -r 25 -i aa/frame-%05d.jpeg -vcodec mjpeg -sameq aa.avi

2009-02-25

LED Display Hacking

Update: Hmm… just had a bit of a revelation. It all seems obvious now: the display isn't driven one column at a time as I assumed (you may have heard that old saying about assuming); instead it's driven one row at a time, that is, the whole row is fed in to the register after which it is lit through one of the TIP32C's. I updated the text below accordingly. Note: the LED modules are marked "MM-300HD 3 Z 6".

Some years ago I got a somewhat old scrolling LED display as a gift from a friend. It's got 15 19 5x7 LED modules (95x7=665 pixels) and an integrated keyboard with which it can be programmed. Because it has no serial link or anything, I took it apart in order to figure out how it works (sorry about that, K). As expected, the device isn't too complicated. There are two PCB's: one that has a microprocessor (or ASIC, not sure), keyboard and display driver circuitry; the other has the actual display modules and a bunch of 7400-series IC's. Connecting the two boards is an 11-way ribbon cable. A bit of reverse engineering revealed the wires' functions:

  1. Clock
  2. Data
  3. Supply voltage
  4. Ground
  5. Display data
  6. Display data
  7. Display data
  8. Display data
  9. Display data
  10. Display data
  11. Display data
LED Display Driver Part

The driver part on the first board has seven BC308B's switching seven TIP32C's each driving one row of LED's. As you may know, in order to keep the circuit simple (and to conserve energy, I suppose), not all the LED's are lit at the same time. Instead they're lit one row at a time, in rapid succession. This has to happen fast enough to beat persistence of vision or we'll observe flickering.

LED Display Display Part

The display part consists of the already mentioned 5x7 LED modules, 75492 hex inverters (16 total), 74164 serial in/parallel out shift registers (12 total) and a number of resistor (mostly pull-up, I suppose).

At least, that's the theory. I have not actually tested any of this.

TBC

2009-01-27

Embedding Google Maps in Web Pages Served as application/xhtml+xml

I ran into trouble while trying to embed a Google Map into a page with Content-Type of application/xhtml+xml. I tried a few different ways of loading the script and initializing the GMap2 object, but they all ended up with Mozilla giving a somewhat cryptic error message (quotemarks exactly as below):

Object cannot be created in this context" code: "9

Turns out that Google's code uses the document.write() method and that that does not work with documents served as application/xhtml+xml. Which means that the pages must be served as text/html. I hope I could find a better solution but for now, that'll have to do.

2009-01-14

iStumbling at +60° 27' 47.54", +26° 56' 13.53"

See map (iStumbler was ran at Kotkankatu-Mariankatu crossing).

Quite a few of the access points have really obvious SSIDs:

iStumbling @Bar Blue 2009-01-14

2009-01-06

Visitor Locator, Take Two

The new version that I hacked together stores number of visits per country and shows the totals when a user clicks a countrys' marker. Visits are stored in an SQLite database, which, as you may know, makes things very easy as there is no server to look after etc. I was thinking of using Berkeley DB, because in an app like this, all that SQL is simply unnecessary sugar, but was lazy in the end (as usual).

Update: Added country flags in place of the same default icon for every country (see: Custom Icons section in Google Maps API).

Update 2: Added tooltip-like functionality, which shows country details in a transient window (label) instead of the default info window. See GxMarker for additional info.

Continuing here from where last nights' script ended. This is just the PHP side of things; Google Maps API examples can be found elsewhere. First we open an SQLite database and create a table for our visitor data if table does not exist:

try {
        $db = new PDO('sqlite:' . $_SERVER['DOCUMENT_ROOT'] . '/../db/visitor-locator.sqlite3');
} catch(PDOException $exception) {
        die($exception->getMessage());
}

$stmt = $db->query('SELECT name FROM sqlite_master WHERE type = \'table\'');
$result = $stmt->fetchAll();
if(sizeof($result) == 0) {
        $db->beginTransaction();
        $db->exec('CREATE TABLE visits (country TEXT, visits INTEGER, lat TEXT, lng TEXT);');
        $db->commit();
}

Next, check if the country is already in the table and if it is, increment the 'visits' field:

$stmt = $db->query('SELECT country, visits FROM visits WHERE country = \'' . $countryname . '\'');
$result = $stmt->fetch();

if($result['country']) {
        $db->beginTransaction();
        $stmt = $db->prepare('UPDATE visits SET visits=:visits, lat=:lat, lng=:lng WHERE country=:country');
        $stmt->bindParam(':country', $countryname, PDO::PARAM_STR);
        $visits = $result['visits'] + 1;
        $stmt->bindParam(':visits', $visits, PDO::PARAM_INT);
        $stmt->bindParam(':lat', $lat, PDO::PARAM_STR);
        $stmt->bindParam(':lng', $lng, PDO::PARAM_STR);
        $stmt->execute();
        $db->commit();
}

If country was not in the table, create a row for it:

else {
        $db->beginTransaction();
        $stmt = $db->prepare('INSERT INTO visits (country, visits, lat, lng) VALUES (:country, :visits, :lat, :lng)');
        $stmt->bindParam(':country', $countryname, PDO::PARAM_STR);
        $visits = 1;
        $stmt->bindParam(':visits', $visits, PDO::PARAM_INT);
        $stmt->bindParam(':lat', $lat, PDO::PARAM_STR);
        $stmt->bindParam(':lng', $lng, PDO::PARAM_STR);
        $stmt->execute();
        $db->commit();
}

And lastly, fetch all rows and form a Javascript array for our client-side script to use:

$result = $db->query('SELECT country, visits, lat, lng FROM visits');

echo "<script type=\"text/javascript\">\n";
echo "//<![CDATA[\n";
echo "var tbl_country = []; var tbl_visits = []; var tbl_lat = []; var tbl_lng = []; var count = 0;\n";
foreach($result->fetchAll() as $row) {
        echo 'tbl_country[count] = \'' . $row['country'] . '\'; ';
        echo 'tbl_visits[count] = \'' . $row['visits'] . '\'; ';
        echo 'tbl_lat[count] = \'' . $row['lat'] . '\'; ';
        echo 'tbl_lng[count] = \'' . $row['lng'] . '\';';
        echo " count++;\n";
}
echo "//]]>\n";
echo "</script>\n";

2009-01-05

MyFirstMashup™

Some friday-night hacking; this gets the users' country from the IP-to-location service provided by hostip.info, looks up coordinates using Google Geocoding service, and displays the result in Google Maps. The geocoding part could also be done in client side.

This PHP snippet is called in the <head> element before any other script. Note: as is the fine tradition here, no error checking is performed. The cURL/parsing part could also be made a bit less redundant (as in making a function of those). But it's friday night. So – reader discretion is advised.

Update: it's definitely not friday, but monday… I was in "friday-mode" because tomorrow is Epiphany (holiday).

Update 2: hostip.info provides coordinates too, at least for some places in its' database (this can be enabled using the position=true parameter in request; you can enter your location into their database in the site, and by doing so help make the database more accurate). Google has far greater coverage, though, so using their geocoding at the moment.

<?php
$ip = $_SERVER['REMOTE_ADDR'];
$url = "http://api.hostip.info/?ip=$ip";

$curl_handle=curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 7);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_HEADER, 0);
$response = curl_exec($curl_handle);
curl_close($curl_handle);

$doc = new DOMDocument();
$parsestatus = $doc->loadXML($response, LIBXML_NOERROR | LIBXML_ERR_NONE);

$countryname = $doc->getElementsByTagName('countryName')->item(0)->nodeValue;

$url = "http://maps.google.com/maps/geo?output=xml&key=YOUR_API_KEY_GOES_HERE&q=" . urlencode($countryname);

$curl_handle=curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 7);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_HEADER, 0);
$response = curl_exec($curl_handle);
curl_close($curl_handle);

$doc = new DOMDocument();
$parsestatus = $doc->loadXML($response, LIBXML_NOERROR | LIBXML_ERR_NONE);

$coordinates = $doc->getElementsByTagName('coordinates')->item(0)->nodeValue;
$coordinatesSplit = split(",", $coordinates);
$lat = $coordinatesSplit[1];
$lng = $coordinatesSplit[0];

echo "<script type=\"text/javascript\">\nvar countryname=\"$countryname\"; // from hostip.info\nvar lat=$lat; // from google geocoding\nvar\
 lng=$lng;\n</script>\n";
?>

Then, when initializing the map, those latitude and longitude variables are fed to the GMap2 .setCenter() method:

function load() {
  if (GBrowserIsCompatible()) {
    var map = new GMap2(document.getElementById("map"));
    map.setMapType(G_HYBRID_MAP);
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.setCenter(new GLatLng(lat, lng), 3);

    var x = new GIcon(G_DEFAULT_ICON);
    x.image = "http://www.google.com/intl/en_us/mapfiles/ms/micons/green-dot.png";
    markerOptions = { icon:x };
    map.addOverlay(new GMarker(new GLatLng(lat, lng), markerOptions));
  }

2009-01-04

URL Fetch API, MiniDom (Google App Engine)

Fetching stuff with the URL Fetch API is simple (especially if one has faith that the source is there and it will deliver inside GAE time limits):

from google.appengine.api import urlfetch
from xml.dom import minidom

def parse(url):
  r = urlfetch.fetch(url)
  if r.status_code == 200:
    return minidom.parseString(r.content)

As is accessing the resulting DOM with MiniDom. Here the source is an Atom feed:

import time

dom = parse(URL)
for entry in dom.getElementsByTagName('entry'):
  try:
    published = entry.getElementsByTagName('published')[0].firstChild.data
    published = time.strftime('%a, %d %b', time.strptime(published, '%Y-%m-%dT%H:%M:%SZ'))
  except IndexError, ValueError:
    pass
  …