[Talk-us] Parsing web map URLs (Re: dirty2osm)
Mark Gray
mark-osmus at hspf.com
Tue Jun 25 13:43:41 UTC 2013
I have spent some time adding the ability to go to and from web
map URLs in my program for viewing tiled maps.
The source code is available at:
http://code.google.com/p/vataviamap/source/browse/trunk/VataviaMap/Shared/clsServer.vb
The part that parses web map URLs is:
Private Const OSMshortlinkPrefix As String = "http://osm.org/go/"
Private Const OSMshortlinkEncoding As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_~"
''' <summary>
''' Given a web map URL, look for the center latitude/longitude, zoom, and bounding box
''' </summary>
''' <param name="aURL">Web map URL to examine</param>
''' <param name="aCenterLatitude">returns latitude at center of area</param>
''' <param name="aCenterLongitude">returns longitude at center of area</param>
''' <param name="aZoom">returns zoom level</param>
''' <param name="aNorth">returns north latitude of bounding box</param>
''' <param name="aWest">returns west longitude of bounding box</param>
''' <param name="aSouth">returns south latitude of bounding box</param>
''' <param name="aEast">returns east longitude of bounding box</param>
''' <returns>
''' True if link was parsed and reasonable values for the center latitude and longitude were found
''' </returns>
Function ParseWebmapURL(ByVal aURL As String, _
ByRef aCenterLatitude As Double,
ByRef aCenterLongitude As Double, _
ByRef aZoom As Integer, _
ByRef aNorth As Double, ByRef aWest As Double, _
ByRef aSouth As Double, ByRef aEast As Double) As Boolean
Try
Dim lBounds As Boolean = False
Dim lURL As String = aURL.ToLower
Dim lArgs() As String
If lURL.StartsWith(OSMshortlinkPrefix) Then
Return ParseOSMshortlink(aURL.Substring(OSMshortlinkPrefix.Length), aCenterLatitude, aCenterLongitude, aZoom)
ElseIf lURL.StartsWith("http://maps.stamen.com/m2i/") Then
lArgs = lURL.Split("/")
If lArgs.Length > 3 AndAlso IsNumeric(lArgs(lArgs.Length - 3)) _
AndAlso IsNumeric(lArgs(lArgs.Length - 2)) _
AndAlso IsNumeric(lArgs(lArgs.Length - 1)) Then
aZoom = Double.Parse(lArgs(lArgs.Length - 3))
aCenterLatitude = Double.Parse(lArgs(lArgs.Length - 2))
aCenterLongitude = Double.Parse(lArgs(lArgs.Length - 1))
End If
ElseIf lURL.StartsWith("geo:") Then
lArgs = lURL.Substring(4).Split(",")
If lArgs.Length > 1 AndAlso IsNumeric(lArgs(0)) AndAlso IsNumeric(lArgs(1)) Then
aCenterLatitude = Double.Parse(lArgs(0))
aCenterLongitude = Double.Parse(lArgs(1))
End If
Else
lArgs = lURL.Split("&"c, "?"c)
For Each lArg As String In lArgs
Dim lArgPart() As String = lArg.Split("=")
If lArgPart.Length = 2 Then
Select Case lArgPart(0)
Case "latitude", "lat", "mlat"
aCenterLatitude = Double.Parse(lArgPart(1))
Case "longitude", "lon", "mlon", "lng", "mlng"
aCenterLongitude = Double.Parse(lArgPart(1))
Case "zoom", "z"
aZoom = Integer.Parse(lArgPart(1))
Case "ll", "q"
Dim ll() As String = lArgPart(1).Split(",")
If ll.Length = 2 AndAlso IsNumeric(ll(0)) Then
'GoogleMaps
DoubleTryParse(ll(0), aCenterLatitude)
DoubleTryParse(ll(1), aCenterLongitude)
End If
Case "spn"
'TODO: parse Google's height,width into zoom
Case "cp"
'Bing
Dim ll() As String = lArgPart(1).Split("~")
If ll.Length = 2 AndAlso IsNumeric(ll(0)) AndAlso IsNumeric(ll(1)) Then
aCenterLatitude = Double.Parse(ll(0))
aCenterLongitude = Double.Parse(ll(1))
End If
Case "lvl" : aZoom = lArgPart(1)
Case "starttop" : aNorth = Double.Parse(lArgPart(1)) : lBounds = True
Case "startbottom" : aSouth = Double.Parse(lArgPart(1))
Case "startleft" : aWest = Integer.Parse(lArgPart(1))
Case "startright" : aEast = Integer.Parse(lArgPart(1))
Case "bbox"
Dim bbox() As String = lArgPart(1).Split(",")
If bbox.Length = 4 Then
lBounds = DoubleTryParse(bbox(0), aWest) _
AndAlso DoubleTryParse(bbox(1), aSouth) _
AndAlso DoubleTryParse(bbox(2), aEast) _
AndAlso DoubleTryParse(bbox(3), aNorth)
End If
End Select
End If
Next
End If
If lBounds Then
aCenterLatitude = (aNorth + aSouth) / 2
aCenterLongitude = (aWest + aEast) / 2
'TODO: compute zoom from (aNorth - aSouth) and/or (aWest - aEast)
End If
If aZoom > ZoomMax Then aZoom = ZoomMax
If aZoom < ZoomMin Then aZoom = ZoomMin
Return aCenterLatitude <> 0 AndAlso aCenterLongitude <> 0
Catch
Return False
End Try
End Function
''' <summary>
''' Parse an OpenStreetMap shortlink code into Latitude, Longitude and Zoom
''' </summary>
''' <param name="aCode">OpenStreetMap shortlink code</param>
''' <param name="aCenterLatitude">returns latitude at center of area</param>
''' <param name="aCenterLongitude">returns longitude at center of area</param>
''' <param name="aZoom">returns zoom level</param>
''' <returns>True if link was parsed and reasonable values for the ByRef arguments were found</returns>
''' <remarks>Based on http://git.openstreetmap.org/?p=rails.git;a=blob_plain;f=lib/short_link.rb;hb=HEAD
''' see also https://help.openstreetmap.org/questions/9566/shortlink-class-in-c</remarks>
Private Function ParseOSMshortlink(ByVal aCode As String, _
ByRef aCenterLatitude As Double, _
ByRef aCenterLongitude As Double, _
ByRef aZoom As Integer) As Boolean
'http://osm.org/go/0MbEUuTq = http://www.openstreetmap.org/?lat=52.50547&lon=13.36932&zoom=16
Dim x As Long = 0
Dim y As Long = 0
Dim z As Long = 0
Dim z_offset As Long = 0
Try
' replace @ in old shortlinks with ~
aCode = aCode.Replace("@", "~")
For Each ch As Char In aCode.ToCharArray
Dim t As Integer = OSMshortlinkEncoding.IndexOf(ch)
If t < 0 Then
z_offset -= 1
Else
For index As Integer = 1 To 3
x <<= 1
If t And 32 Then x += 1
t <<= 1
y <<= 1
If t And 32 Then y += 1
t <<= 1
Next
z += 3
End If
Next
' pack the coordinates out to their original 32 bits.
x <<= (32 - z)
y <<= (32 - z)
' project the parameters back to their coordinate ranges.
aCenterLongitude = (x * 360.0 / 2 ^ 32) - 180.0
aCenterLatitude = (y * 180.0 / 2 ^ 32) - 90.0
aZoom = z - 8 - (z_offset Mod 3)
Return aCenterLatitude > -90 AndAlso aCenterLatitude < 90 AndAlso _
aCenterLongitude >= -180 AndAlso aCenterLongitude <= 180
Catch
Return False
End Try
End Function
More information about the Talk-us
mailing list