-
Notifications
You must be signed in to change notification settings - Fork 4
/
GEOSage.vb
247 lines (199 loc) · 7.66 KB
/
GEOSage.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
''
'' FILE: GEOSage.bas
'' AUTHOR: Jaewoong Mun (happybono@outlook.com)
'' CREATED: February 05, 2020
''
'' Released to the public domain
''
Option Explicit
' domain and URL for Google Geocoding API
Public Const gstrGeocodingDomain = "https://maps.googleapis.com"
Public Const gstrGeocodingURL = "/maps/api/geocode/xml?"
' set gintType = 1 to use the Enterprise Geocoder (requires clientID and Google Maps Geocoding API Key)
' set gintType = 2 to use the API Premium Plan (requires Google Maps Geocoding API Key)
' leave gintType = 0 to use the free-ish Google geocoder (requires Google Maps Geocoding API Key!
' see https://developers.google.com/maps/documentation/geocoding/get-api-key)
Public Const gintType = 0
' key for Enterprise Geocoder or API Premium Plan or free-ish geocoder
Public Const gstrKey = "[Your Google Maps API Key]"
' clientID for Enterprise Geocoder (if applicable)
Public Const gstrClientID = "[Your Google Maps ClientID]"
' kludge to not overdo the API calls and add a delay
#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If
Public Function ADDRGEOCODE(address As String) As String
Dim strAddress As String
Dim strQuery As String
Dim strLatitude As String
Dim strLongitude As String
Dim strQueryBland As String
strAddress = URLEncode(address)
'assemble the query string
strQuery = gstrGeocodingURL
strQuery = strQuery & "address=" & strAddress
If gintType = 0 Then ' free-ish Google Geocoder - required an API key!
strQuery = strQuery & "&key=" & gstrKey
ElseIf gintType = 1 Then ' Enterprise Geocoder
strQuery = strQuery & "&client=" & gstrClientID
strQuery = strQuery & "&signature=" & Base64_HMACSHA1(strQuery, gstrKey)
ElseIf gintType = 2 Then ' API Premium Plan
strQuery = strQuery & "&key=" & gstrKey
End If
'define XML and HTTP components
Dim googleResult As New MSXML2.DOMDocument60
Dim googleService As New MSXML2.XMLHTTP60
Dim oNodes As MSXML2.IXMLDOMNodeList
Dim oNode As MSXML2.IXMLDOMNode
Sleep (5)
'make sure to have create HTTP request to query URL
googleService.Open "GET", gstrGeocodingDomain & strQuery, False
googleService.send
googleResult.LoadXML (googleService.responseText)
Set oNodes = googleResult.getElementsByTagName("geometry")
If oNodes.Length = 1 Then
For Each oNode In oNodes
Debug.Print oNode.Text
strLatitude = oNode.ChildNodes(0).ChildNodes(0).Text
strLongitude = oNode.ChildNodes(0).ChildNodes(1).Text
ADDRGEOCODE = strLatitude & "," & " " & strLongitude
Next oNode
Else
ADDRGEOCODE = "Not Found (You may have reached your daily limit. Please check your daily quota and try again.)"
End If
End Function
' Public Function URLEncode(StringVal As String, Optional SpaceAsPlus As Boolean = False) As String
' Dim StringLen As Long: StringLen = Len(StringVal)
'
' If StringLen > 0 Then
' ReDim result(StringLen) As String
' Dim i As Long, CharCode As Integer
' Dim Char As String, Space As String
'
' If SpaceAsPlus Then Space = "+" Else Space = "%20"
'
' For i = 1 To StringLen
' Char = Mid$(StringVal, i, 1)
' CharCode = asc(Char)
' Select Case CharCode
' Case 97 To 122, 65 To 90, 48 To 57, 45, 46, 95, 126
' result(i) = Char
' Case 32
' result(i) = Space
' Case 0 To 15
' result(i) = "%0" & Hex(CharCode)
' Case Else
' result(i) = "%" & Hex(CharCode)
' End Select
' Next i
' URLEncode = Join(result, "")
' End If
' End Function
Public Function URLEncode(ByVal StringVal As String, Optional SpaceAsPlus As Boolean = False) As String
Dim bytes() As Byte, b As Byte, i As Integer, space As String
If SpaceAsPlus Then space = "+" Else space = "%20"
If Len(StringVal) > 0 Then
With New ADODB.Stream
.Mode = adModeReadWrite
.Type = adTypeText
.Charset = "UTF-8"
.Open
.WriteText StringVal
.Position = 0
.Type = adTypeBinary
.Position = 3 ' skip BOM
bytes = .Read
End With
ReDim result(UBound(bytes)) As String
For i = UBound(bytes) To 0 Step -1
b = bytes(i)
Select Case b
Case 97 To 122, 65 To 90, 48 To 57, 45, 46, 95, 126
result(i) = Chr(b)
Case 32
result(i) = space
Case 0 To 15
result(i) = "%0" & Hex(b)
Case Else
result(i) = "%" & Hex(b)
End Select
Next i
URLEncode = Join(result, "")
End If
End Function
Public Function REVSGEOCODE(lat As String, lng As String) As String
Dim strAddress As String
Dim strLat As String
Dim strLng As String
Dim strQuery As String
Dim strLatitude As String
Dim strLongitude As String
strLat = URLEncode(lat)
strLng = URLEncode(lng)
'assembles the query string
strQuery = gstrGeocodingURL
strQuery = strQuery & "latlng=" & strLat & "," & strLng
If gintType = 0 Then ' free-ish Google Geocoder - required an API key!
strQuery = strQuery & "&key=" & gstrKey
ElseIf gintType = 1 Then ' Enterprise Geocoder
strQuery = strQuery & "&client=" & gstrClientID
strQuery = strQuery & "&signature=" & Base64_HMACSHA1(strQuery, gstrKey)
ElseIf gintType = 2 Then ' API Premium Plan
strQuery = strQuery & "&key=" & gstrKey
End If
'define XML and HTTP components
Dim googleResult As New MSXML2.DOMDocument60
Dim googleService As New MSXML2.XMLHTTP60
Dim oNodes As MSXML2.IXMLDOMNodeList
Dim oNode As MSXML2.IXMLDOMNode
Sleep (5)
'create HTTP request to query URL - make sure to have
googleService.Open "GET", gstrGeocodingDomain & strQuery, False
googleService.send
googleResult.LoadXML (googleService.responseText)
Set oNodes = googleResult.getElementsByTagName("formatted_address")
If oNodes.Length > 0 Then
REVSGEOCODE = oNodes.Item(0).Text
Else
REVSGEOCODE = "Not Found (You may have reached your daily limit. Please check your daily quota and try again.)"
End If
End Function
Public Function Base64_HMACSHA1(ByVal strTextToHash As String, ByVal strSharedSecretKey As String)
Dim asc As Object
Dim enc As Object
Dim TextToHash() As Byte
Dim SharedSecretKey() As Byte
Dim bytes() As Byte
Set asc = CreateObject("System.Text.UTF8Encoding")
Set enc = CreateObject("System.Security.Cryptography.HMACSHA1")
strSharedSecretKey = Replace(Replace(strSharedSecretKey, "-", "+"), "_", "/")
SharedSecretKey = Base64Decode(strSharedSecretKey)
enc.Key = SharedSecretKey
TextToHash = asc.Getbytes_4(strTextToHash)
bytes = enc.ComputeHash_2((TextToHash))
Base64_HMACSHA1 = Replace(Replace(Base64Encode(bytes), "+", "-"), "/", "_")
End Function
Public Function Base64Decode(ByVal strData As String) As Byte()
Dim objXML As MSXML2.DOMDocument60
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument60
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.Text = strData
Base64Decode = objNode.nodeTypedValue
Set objNode = Nothing
Set objXML = Nothing
End Function
Public Function Base64Encode(ByRef arrData() As Byte) As String
Dim objXML As MSXML2.DOMDocument60
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument60
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.nodeTypedValue = arrData
Base64Encode = objNode.Text
Set objNode = Nothing
Set objXML = Nothing
End Function