Show Coordinates in Google Maps

By Ilya Dogolazky Last update Jan 23, 2008 — Installed 2,428 times.
// This JavaScript file is automatically generated by js-tool.
// Do NOT read it! Do NOT edit it! Edit the source code instead.


// Show Coordinates in Google Maps 0.0.1 (alpha) 2008-01-23
// ------------------------------------------
// Copyright (c) 2008 Ilya Dogolazky
// Released under the GPL license, see http://www.gnu.org/copyleft/gpl.html for details
// ------------------------------------------
// ==UserScript==
// @name           Show Coordinates in Google Maps
// @namespace      http://www.math.uni-bonn.de/people/ilyad/gm/coordinates
// @include        http://maps.google.tld/*
// ==/UserScript==

const nbsp = String.fromCharCode(160) ;

// File Debug.js
Debug.level = function(l) // 0:nothing, 1:alerts only; 2:errors+alerts 3: alerts, errors and warnings 4:all messages
{
  Debug.current_level = l ;
}

Debug.gm = function(level, cl, msg)
{
  if(cl>level)
  {
    for(var i=0, ar=[]; i<msg.length; ++i)
      ar.push(msg[i]) ;
    var message = ar.join("") ;

    // GM_log(message, 3-level) ;
    // oops! GM_log accepts only one parameter!
    // http://wiki.greasespot.net/index.php?title=GM_log&diff=1764&oldid=1762 :-(
    
    if(level==3) GM_log(message) ;
    else if(level==2) GM_log("WARNING: "+message) ;
    else if(level==1) GM_log("ERROR!\n"+message) ;
    else if(level==0)
    {
      var answer = window.confirm(message+"\n\n---------------\n\n"+"Click 'ok' to continue, 'cancel' to abort script") ;
      if(!answer)
        throw new Error("Aborted: "+message) ;
    }
    else
      window.alert("Debug.gm(): Invalid debugging level ("+level+") Message:\n"+message) ;
  }
}

Debug.level(4) ;

Debug.log = function() { Debug.gm(3, Debug.current_level, arguments) ; }
Debug.warn = function() { Debug.gm(2, Debug.current_level, arguments) ; }
Debug.error = function() { Debug.gm(1, Debug.current_level, arguments) ; }
Debug.alert  = function() { Debug.gm(0, Debug.current_level, arguments) ; }


Debug.prototype.log = function() { this.log_member(3, arguments) ; }
Debug.prototype.warn = function() { this.log_member(2, arguments) ; }
Debug.prototype.error = function() { this.log_member(1, arguments) ; }
Debug.prototype.alert  = function() { this.log_member(0, arguments) ; }

Debug.prototype.log_member = function(level, msg)
{
  Debug.gm(level, this.enabled(), msg) ;
}

Debug.prototype.enabled = function()
{
  if(this.absolute)
    return this.current_level ;
  else
    return Math.min(this.current_level, Debug.current_level) ;
}

function Debug(level)
{
  this.absolute = (level<0) ;
  if(this.absolute)
    level = -level ;
  this.current_level = level ;
}



// vim:tw=0:smartindent

// End of file Debug.js
// File Xpath.js
Xpath.list = function(xpath, root, order)
{
  if(!root)
    root = window.document ;
  var result = [] ;
  var snapshot = document.evaluate(xpath, root, null, (order ? XPathResult.ORDERED_NODE_SNAPSHOT_TYPE : XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE), null) ;
  for(var i=0; i<snapshot.snapshotLength; ++i)
    result.push(snapshot.snapshotItem(i)) ;
  return result ;
}

Xpath.node = function(xpath, root)
{
  return Xpath.list(xpath, root, false)[0] ;
}

Xpath.aNode = function (hash, root)
{
  var list = Xpath.aList(hash, root) ;
  if(list.length==0)
    return null ;
  if(list.length>1)
    GM_log("Multiply nodes found in Xpath.aNode("+hash+")") ;
  return list[0] ;
}

Xpath.aList = function(list, root, order)
{
  var xpath = Xpath.aXpath(list) ;
  return Xpath.list(xpath, root, order) ;
}

Xpath.aXpath = function (list)
{
  var s = [] ;
  while(list.length>0)
  {
    var key = list.shift() ;
    var value = list.shift() ;
    var at = "@" + key ;
    s.push((value==null || value=="*") ? at : at+"='" + value + "'") ;
  }
  return "descendant::*["+s.join(" and ")+"]" ;
}

function Xpath()
{}

// End of file Xpath.js
// File Coordinates.js
// File Vector.js
function Vector(object, first, second)
{
  if(object instanceof Array)
  {
    this.values = [object[0], object[1]] ;
    this.names = [first || "_x", second || "_y"] ;
  }
  else
  {
    this.names = [first, second] ;
    this.values = [] ;
    for(var i=0; i<2; ++i)
    {
      var x = this.names[i];
      if(object[x]!=null)
        var re = object[x].match("(?:^| )(-?\\d+)(?:px)?(?: |$)") ;
      else
        var re = object.toString().match("(?:^| )"+x+": (-?\\d+)px;(?: |$)") ;
      if(!re)
        return null ;
      var v = parseInt(re[1]) ;
      if(isNaN(v))
        return null ;
      this.values.push(v) ;
    }
  }
  // Debug.log("Vector constructed from object ", object, "\n", this.names.toString(), "\n", this.values.toString())
  // Debug.log("Vector constructed from object ", object, "\n", this.toString())
}

Vector.prototype.x = function()
{
  return this.values[0] ;
}
Vector.prototype.y = function()
{
  return this.values[1] ;
}

Vector.prototype.plus = function(w)
{
  return new Vector([this.x()+w.x(), this.y()+w.y()]) ;
}

Vector.prototype.minus = function(w)
{
  return new Vector([this.x()-w.x(), this.y()-w.y()]) ;
}

Vector.prototype.toString = function()
{
  return "(" + this.names[0] + ": " + this.values[0] + ", " +
               this.names[1] + ": " + this.values[1] +
         ")" ;
}

// vim:tw=0:smartindent

// End of file Vector.js
// File Tile.js
// File Uri.js
function Uri(str)
{
  var t = str.match(/^(.*?)#(.*)$/) ;
  if(t)
  {
    str = t[1] ;
    this.fragment = t[2] ;
  }
  this.location = str ;
  t = str.match(/^(.*?)\?(.*)$/) ;
  if(t)
  {
    str = t[1] ;
    var q = t[2] ;
  }
  this.path = str ;
  this.query = new Object ;
  if(q)
  {
    for each(var qq in q.split('&'))
    {
      var i = qq.search("=") ;
      if(i==-1)
      {
        var key = qq ;
        var value = undefined
      }
      else
      {
        var key = qq.substr(0,i) ;
        var value = qq.substr(i+1) ;
      }
      this.query[key] = value ;
    }
  }
}

Uri.prototype.originalLocation = function()
{
  return this.location ;
}

Uri.prototype.toString = function()
{
  var res = this.path ;
  var qq = [] ;
  for(var key in this.query)
  {
    var q = key+'' ;
    var value = this.query[key] ;
    if(value!=undefined)
      q += '=' + value ;
    qq.push(q)
  }
  if(qq.length)
    res += "?" + qq.join("&") ;
  if(this.fragment)
    res += "#" + this.fragment ;
  return res ;
}

if(new Debug(0).enabled())
{
  var u = new Uri(window.location.toString()) ;
  alert(Dumper(u)+"\n----\n"+u.toString()) ;
}

// End of file Uri.js
// use Debug: already included
// use Vector: already included

Tile.rad2deg = function(x) { return x*(180/Math.PI); } 
Tile.deg2rad = function(x) { return x/(180/Math.PI); }

// (-pi/2, pi/2) --> (-infty,+infty)
Tile.f1 = function(x) { return 0.5 * Math.log((1+Math.sin(x))/(1-Math.sin(x))) ; }
// (-pi/2, pi/2) <-- (-infty,+infty)
Tile.f2 = function(x) { return Math.asin((Math.exp(2*x)-1)/(Math.exp(2*x)+1)) ; }


Tile.TILE_SIZE = 256 ;
Tile.MAXZOOM = 21 ; // Can be changed
Tile.MAX_MAP_ZOOM = 17 ; // Do not change!
Tile.TOTAL = (1<<Tile.MAXZOOM) * Tile.TILE_SIZE ;
Tile.MAX_LATITUDE = Tile.rad2deg(Tile.f2(Math.PI)) ;

Tile.grain2latitude = function(y2)
{
  var y1 = (Tile.TOTAL / 2) - y2 ;
  var f1 = y1 * Math.PI / (Tile.TOTAL / 2) ;
  return Tile.rad2deg(Tile.f2(f1)) ;
}

Tile.grain2longitude = function(g)
{
  return (g-(Tile.TOTAL/2)) / (Tile.TOTAL / 360) ;
}

Tile.xy={q:{x:0,y:0}, r:{x:1,y:0}, t:{x:0,y:1}, s:{x:1,y:1}} ;

Tile.qrst2xyz = function(qrst)
{
  var s = {x:0, y:0, z:qrst.length} ;
  for(var i=0; i<qrst.length; ++i)
  {
    var p = qrst.charAt(i) ;
    for each(var v in ["x", "y"])
    {
      s[v] <<= 1 ;
      s[v] += Tile.xy[p][v] ;
    }
  }
  return s ;
}

function Tile(url)
{
  var query = new Uri(url).query ;
  if(query.t!=null)
  {
    var qrst = query.t.substr(1, query.t.length-1) ;
    var xyz = Tile.qrst2xyz(qrst) ;
  }
  else
  {
    var xyz = {
      x: parseInt(query.x),
      y: parseInt(query.y),
      z:Tile.MAX_MAP_ZOOM-parseInt(query.zoom)
    } ;
  }
  for each(var v in ["x", "y", "z"])
    if(isNaN(this[v] = xyz[v]))
      return null ;
}

Tile.prototype.pixel2lonlat = function (v)
{
  var grain_x = ((this.x * Tile.TILE_SIZE) + v.x()) * (1<<(Tile.MAXZOOM-this.z)) ;
  var grain_y = ((this.y * Tile.TILE_SIZE) + v.y()) * (1<<(Tile.MAXZOOM-this.z)) ;
  var lon = Tile.grain2longitude(grain_x) ;
  var lat = Tile.grain2latitude(grain_y) ;
  while(lon>180)
    lon -= 360 ;
  while(lon<-180)
    lon += 360 ;
  return new Vector([lon, lat], "lon", "lat") ;
}

Tile.precision = function(z, unit) // z is the same as "z" property, unit = 1 for degrees or 1/3600 for seconds
{
  var units_in_pixel = (360/unit) / (Tile.TILE_SIZE*(1<<z)) ;
  var prec = 0 ;
  while(units_in_pixel < 1)
  {
    ++ prec ;
    units_in_pixel *= 10 ;
  }
  return prec ;
}

Tile.format_deg = function(lonlat, prec)
{
  var x = lonlat.x() ;
  var y = lonlat.y() ;
  var dot = (prec>0) ? 1 : 0 ;
  return print_s(x.toFixed(prec), prec+dot+4) + nbsp + print_s(y.toFixed(prec), prec+dot+3) ;
}

function print_s(str, prec)
{
  var res = "" ;
  for(var i=0; i<prec-str.length; ++i)
    res += nbsp;
  return res + str ;
}

// vim:tw=0:smartindent

// End of file Tile.js

const image_o_png ="%89%50%4e%47%0d%0a%1a%0a%00%00%00%0d%49%48%44%52%00%00%00%0d%00%00%00%0d%08%06%00%00%00%72%eb%e4%7c%00%00%00%06%62%4b%47%44%00%00%00%00%00%00%f9%43%bb%7f%00%00%00%09%70%48%59%73%00%00%0b%13%00%00%0b%13%01%00%9a%9c%18%00%00%00%07%74%49%4d%45%07%d8%01%17%14%36%15%3d%f9%0c%46%00%00%00%1d%74%45%58%74%43%6f%6d%6d%65%6e%74%00%43%72%65%61%74%65%64%20%77%69%74%68%20%54%68%65%20%47%49%4d%50%ef%64%25%6e%00%00%00%55%49%44%41%54%28%cf%b5%92%41%0e%80%40%08%03%a7%86%0f%ec%ff%1f%5b%0f%1e%44%57%c9%86%e8%1c%49%d3%42%03%9c%d8%b6%01%73%65%9a%6f%34%88%07%e7%0a%03%c4%91%bc%a0%4e%3a%bd%24%e9%ee%fe%1f%1e%e3%db%84%d6%4d%53%7b%92%ca%e6%00%22%8b%aa%fa%b3%2e%d2%2a%5e%3c%a7%f7%46%3b%27%fe%24%26%00%d7%4a%70%00%00%00%00%49%45%4e%44%ae%42%60%82";
function Coordinates(o)
{
  for each(var key in ['anchor', 'map'])
    this[key] = o[key] ;
  this.container = Xpath.node("./div[1]/div[1]", this.map) ;
  this.lala = document.createTextNode("lala") ;
  var space = document.createTextNode(" " + String.fromCharCode(0x263A)+ " ")
  var tt = document.createElement("tt") ;
  tt.appendChild(this.lala) ;
  this.anchor.parentNode.appendChild(space) ;
  this.anchor.parentNode.appendChild(tt) ;
  this.origin = document.createElement("img") ;
  // this.origin.innerHTML = "O" ;
  this.origin.src = "data:image/png," + image_o_png ;
  this.origin.style.position = "absolute" ;
  this.origin.style.left = "200px" ;
  this.origin.style.top = "100px" ;
  this.origin.id = "OOO" ;
  var s = window.getComputedStyle(this.map, "") ;
  var m = new Vector(s, "width", "height") ;
  this.origin.style.left = Math.floor(m.x()/2)+"px" ;
  this.origin.style.top  = Math.floor(m.y()/2)+"px" ;
  // Debug.alert("h", s.height, "w", s.width ) ;
  this.map.appendChild(this.origin) ;
  this.img = null ;
  for each(var i in Xpath.list('./div[1]/div[1]/div[2]/div[1]/img',this.map))
  {
    if(this.isImageOfMain(i, i.src))
    {
      this.img = i ;
      this.compute(i.src, i.style, this.container.style) ;
      break ;
    }
  }
}

Coordinates.prototype.isImageOfMain = function(image, src)
{
  if(src.match(/transparent.png$/))
    return false ;
  if(image.parentNode.parentNode.parentNode.parentNode.parentNode!=this.map)
    return false ;
  var tile = new Tile(src) ;
  if(tile)
  {
    this.tile = tile ;
    return true ;
  }
  else
    return false ;
}

Coordinates.prototype.compute = function(tile_src, tile_sty, cont_sty)
{
  if(tile_src == null)
  {
    var txt = "" ;
    for(var i=0; i<this.lala.nodeValue.length; ++i)
      txt += nbsp ;
  }
  else
  {
    var t = new Vector(tile_sty, "left", "top") ;
    // Debug.log("t=", t) ;
    var c = new Vector(cont_sty, "left", "top") ;
    // Debug.log("c=", c) ;
    var o = new Vector(this.origin.style, "left", "top") ;
    // Debug.log("o=", o) ;
    var o_middle = new Vector([6,6]) ;
    var sum = o.minus(c).minus(t).plus(o_middle) ;
    // txt = ("["+sum.x()+","+sum.y()+"]") + " in " + (tile_src) ;
    // var tile = new Tile(tile_src) ;
    var lonlat = this.tile.pixel2lonlat(sum) ;
    // txt = ("["+lonlat.x()+","+lonlat.y()+"]") ;
    // txt += " " + Tile.precision(this.tile.z, 1) + " " + Tile.precision(this.tile.z, 1.0/3600) + " " ;
    // txt = Tile.format(lonlat,   Tile.precision(this.tile.z, 1), true) ;
    txt = Tile.format_deg(lonlat, Tile.precision(this.tile.z, 1)) ;
  }
  this.lala.nodeValue = txt ;
}

/*
function parseStyleValue(str, id)
{
  var x = str.match("\\b"+id+"\\b: (-?\\d+)px") ;
  var value = parseInt(x[1]) ;
  return value ;
}
*/

Coordinates.prototype.handleEvent = function(event)
{
  var et = event.target ;
  switch(event.type)
  {
    /*
    case "DOMNodeInserted":
      if(et.nodeName.toLowerCase()=="img")
        this.lala.nodeValue = "Inserted: " +  et.nodeName + " src=" + et.src  ;
      break ;
    */
    case "DOMNodeRemoved":
      if(et==this.img)
      {
        Debug.log("Removed NODE: ", this.img.src) ;
        this.img = null ;
        this.compute(null) ;
      }
      /*
      if(et.nodeName.toLowerCase()=="img")
        this.lala.nodeValue = "Remove " + et.nodeName + " src=" + et.src ;
      */
      break ;
    case "DOMAttrModified":
      if(!this.img)
      {
        if(et.nodeName.toLowerCase()=="img" && event.attrName.toLowerCase()=="src" && this.isImageOfMain(et, event.newValue)) // && event.newValue.match("^http://mt"))
        {
          this.img = et ;
          this.compute(event.newValue, this.img.style, this.container.style) ;
          // Global.container = Global.img.parentNode.parentNode.parentNode ;
          Debug.log("Loaded: ", event.newValue) ;
        }
      }
      if(this.img && this.container==et && event.attrName.toLowerCase()=="style")
      {
        // Debug.log(event.attrName, " := ", event.newValue) ;
        this.compute(this.img.src, this.img.style, event.newValue) ;
        // Debug.log("Moved: ", event.attrName, "-->", event.newValue, "\nimg.src=Röttgen, Bonn, Germany", this.img.src) ;
      }
      if(this.img && et==this.img && event.attrName.toLowerCase()=="style")
      {
        Debug.log(this.img.src, " --- this.img.style:\n", "from ", event.oldValue, "\n", "to ", event.newValue) ;
      }
      /*
      if(this.img && this.image==et && event.attrName.toLowerCase()=="src")
      {
         Debug.log("Reloaded: ", event.newValue) ;
      }
      if(this.img && this.image==et)
      {
        this.lala.nodeValue = event.attrName+  " := " + event.newValue ;
      }
      */
      if(event.attrName.toLowerCase() == "src" && et==this.img) // && !event.newValue.match(/transparent.png$/))
      {
        if(!this.isImageOfMain(this.img, event.newValue))
        {
          Debug.log("Removed: ", event.oldValue) ;
          this.img = null ;
          this.compute(null) ;
        }
        else
        {
          Debug.log("Reloaded: ", event.newValue) ;
          this.compute(event.newValue, this.img.style, this.container.style) ;
        }
      }
      //this.lala.nodeValue = ()et.nodeName + "." + event.attrName + " := " + event.newValue  ;
      break ;
  }
}

// vim:tw=0:smartindent

// End of file Coordinates.js

Debug.level(0) ;

window.addEventListener("load", function(){setTimeout(Main,1000);}, false) ;

function Main()
{
  if(window.parent!=window)
    return ;
  var anchor = Xpath.node('//a/img[@class="bar_icon_link"]/..') ;
  var map = Xpath.node('//div[@id="map"]') ;
  var co = new Coordinates({anchor: anchor, map: map}) ;
  document.addEventListener("DOMAttrModified", co, false) ;
  document.addEventListener("DOMNodeRemoved",  co, false) ;
  document.addEventListener('DOMNodeInserted', co, false) ;
  Main.coordinates = co ;
}



// vim:tw=0:smartindent