Missing Google JSON details - json

Referencing this https://developers.google.com/maps/documentation/places/#PlaceDetails
I was expecting the JSON result to always hold all of the address_components details. However, one of my searches ended up missing street_number, postal_code and one of the others. Looking in intellisense revealed one of the address_components returned held an administrative_area_level_2.
This has thrown me into a loop because I was always expecting to have those original values returned even if they were maybe empty. I was also hardcoding the expected return like so:
array.result.address_components(0).long_name
Since I can't expect the components to return all the results, I need a new way to appropriately access them and figure out what to do when I'm missing some.

Here's the case statement I used to handle this problem. It worked, but not sure if it's the most elegant solution.
Dim street As String = ""
Dim city As String = ""
Dim state As String = ""
Dim country As String = ""
Dim zip As String = ""
Dim phone As String = ""
Dim website As String = ""
' No guareentee to the order of even if the data will be there so we check each type. '
For j = 0 To array.result.address_components.Length - 1
Select Case array.result.address_components(j).types(0)
Case "street_number"
street += array.result.address_components(j).long_name()
Case "route"
street += " " & array.result.address_components(j).long_name()
Case "locality"
city = array.result.address_components(j).long_name()
Case "administrative_area_level_1"
state = array.result.address_components(j).long_name()
Case "country"
country = array.result.address_components(j).long_name()
Case "postal_code"
zip = array.result.address_components(j).long_name()
End Select
Next
I basically cycle through the address_components and check their types value. If the value is one I'm looking for I assign it. I later call Trim() on street to remove that white space I add if there's no street number.

Related

Find the position of the first occurrence of any number in string (if present) in MS Access

In MS Access I have a table with a Short Text field named txtPMTaskDesc in which some records contains numbers, and if they do, at different positions in the string. I would like to recover these numbers from the text string if possible for sorting purposes.
There are over 26000 records in the table, so I would rather handle it in a query over using VBA loops etc.
Sample Data
While the end goal is to recover the whole number, I was going to start with just identifying the position of the first numerical value in the string. I have tried a few things to no avail like:
InStr(1,[txtPMTaskDesc],"*[0-9]*")
Once I get that, I was going to use it as a part of a Mid() function to pull out it and the character next to it like below. (its a bit dodgy, but there is never more than a two-digit number in the text string)
IIf(InStr(1,[txtPMTaskDesc],"*[0-9]*")>0,Mid([txtPMTaskDesc],InStr(1,[txtPMTaskDesc],"*[0-9]*"),2)*1,0)
Any assistance appreciated.
If data is truly representative and number always preceded by "- No ", then expression in query can be like:
Val(Mid(txtPMTaskDesc, InStr(txtPMTaskDesc, "- No ") + 5))
If there is no match, a 0 will return, however, if field is null, the expression will error.
If string does not have consistent pattern (numbers always in same position or preceded by some distinct character combination that can be used to locate position), don't think can get what you want without VBA. Either loop through string or explore Regular Expressions aka RegEx. Set reference to Microsoft VBScript Regular Expressions x.x library.
Function GetNum(strS AS String)
Dim re As RegExp, Match As Object
Set re = New RegExp
re.Pattern = "[\d+]+"
Set Match = re.Execute(strS)
GetNum = Null
If Match.Count > 0 Then GetNum = Match(0)
End Function
Input of string "Fuel Injector - No 1 - R&I" returns 1.
Place function in a general module and call it from query.
SELECT table.*, GetNum(Nz(txtPMTaskDesc,"")) AS Num FROM table;
Function returns Null if there is no number match.
Well, does the number you want ALWAYS have a - No xxxx - format?
If yes, then you could have this global function in VBA like this:
Public Function GNUM(v As Variant) As Long
If IsNull(v) Then
GNUM = 0
Exit Function
End If
Dim vBuf As Variant
vBuf = Split(v, " - No ")
Dim strRes As String
If UBound(vBuf) > 0 Then
strRes = Split(vBuf(1), "-")(0)
GNUM = Trim(strRes)
Else
GNUM = 0
End If
End Function
Then your sql will be like this:
SELECT BLA, BLA, txtPMTaskDesc, GNUM([txtPMTaskDesc] AS TaskNum
FROM myTable
So you can create/have a public VBA function, and it can be used in the sql query.
It just a question if " - No -" is ALWAYS that format, then THEN the number follows this
So we have "space" "-" "space" "No" "space" "-" -- then the number and the " -"
How well this will work depends on how consistent this text is.

GeoLocation - Parsing //Formatted_Address?

I am playing with a new function in an AccessDB app to return Lat/Lon info from Hospital names. The following function provides what I need when I provide a Name & Address. I noticed (unexpected) the function returns a formatted address even if I provide JUST a valid hospital name. I think I can exploit this to backfill address info into my database.
It appears that Geocode.sRetAddress = .selectSingleNode("//formatted_address").Text is "mostly" consistent and easilly parsed to grab Address/City/State/ZIP info using "," as a delimiter. My complication is the rare occasion where a "Floor Number" is included in the formatted address string. My parsing routine fails.
I found this routine (not mine):
Option Explicit
Option Compare Database
'Public Type containing the geocoding of the postal address
Public Type tGeocodeResult
dLatitude As Double
dLongitude As Double
sRetAddress As String
sAccuracy As String
sStatus As String
End Type
'---------------------------------------------------------------------------------------
' Procedure : Geocode with Google Geocoding API v3
' Version : 1.01
' DateTime : 03/03/2011
' Author : Philben
' Purpose : converting addresses into geographic coordinates
' Parameter : No mandatory. string format or NULL
' Reference : http://code.google.com/intl/fr-FR/apis/maps/documentation/geocoding/index.html
' Remark : Query limit of 2,500 geolocation requests per day
' : A good accuracy is different of a good geocoding !!!
' : Minimum delay between two queries : >= 200 ms
'---------------------------------------------------------------------------------------
Public Function Geocode(Optional ByVal vAddress As Variant = Null, _
Optional ByVal vTown As Variant = Null, _
Optional ByVal vPostCode As Variant = Null, _
Optional ByVal vRegion As Variant = Null, _
Optional ByVal sCountry As String = "UNITED STATES+") As tGeocodeResult
On Error GoTo catch
Dim oXmlDoc As Object
Dim sUrl As String, sFormatAddress As String
If Not IsNull(vAddress) Then vAddress = Replace(vAddress, ",", " ")
sFormatAddress = (vAddress + ",") & _
(vTown + ",") & _
(vRegion + ",") & _
(vPostCode + ",") & _
sCountry
'To create the URL
sUrl = "http://maps.googleapis.com/maps/api/geocode/xml?address=" & sFormatAddress & "&sensor=false"
''XMLDOM to get the XML response
Set oXmlDoc = CreateObject("Microsoft.XMLDOM")
With oXmlDoc
.Async = False
If .Load(sUrl) And Not .selectSingleNode("GeocodeResponse/status") Is Nothing Then
'Status code
Geocode.sStatus = .selectSingleNode("GeocodeResponse/status").Text
'If a result is returned
If Not .selectSingleNode("GeocodeResponse/result") Is Nothing Then
'formatted_address
Geocode.sRetAddress = .selectSingleNode("//formatted_address").Text
'Accuracy
Geocode.sAccuracy = .selectSingleNode("//location_type").Text
'Latitude and longitude
Geocode.dLatitude = Val(.selectSingleNode("//location/lat").Text)
Geocode.dLongitude = Val(.selectSingleNode("//location/lng").Text)
End If
End If
End With
Set oXmlDoc = Nothing
Exit Function
catch:
Set oXmlDoc = Nothing
Err.Raise Err.Number, , Err.Description
End Function
Example Results (Geocode.sRetAddress - formatted address):
good: 100 S Raymond Ave, Alhambra, CA 91801, USA
good: 3040 Salt Creek Ln, Arlington Heights, IL 60005, USA
bad: 4th floor, 2450 Ashby Ave, Berkeley, CA 94705, USA
Question
Any clue if the "Floor" component of the Formatted Address can be excluded, or alternatively explicitly return JUST the desired components?
Thanks,
Mark Pelletier
PS> I am currently counting the number of "," in the string and conditionally handling the parsing task. But As a general purpose approach, there will likely be other exceptions I have not encountered yet.
I'm a bit of a beginner in XPath, but I think I can solve this:
Instead of:
'formatted_address
Geocode.sRetAddress = .selectSingleNode("//formatted_address").Text
Use:
'Build an address:
Geocode.sRetAddress = oXMLDoc.selectSingleNode("descendant::address_component[type='street_number']/short_name").text
Geocode.sRetAddress = Geocode.sRetAddress & " " oXMLDoc.selectSingleNode("descendant::address_component[type='route']/short_name").text
Geocode.sRetAddress = Geocode.sRetAddress & ", " oXMLDoc.selectSingleNode("descendant::address_component[type='locality']/short_name").text
Geocode.sRetAddress = Geocode.sRetAddress & ", " oXMLDoc.selectSingleNode("descendant::address_component[type='administrative_area_level_1']/short_name").text
Geocode.sRetAddress = Geocode.sRetAddress & " " oXMLDoc.selectSingleNode("descendant::address_component[type='postal_code']/short_name").text
Geocode.sRetAddress = Geocode.sRetAddress & ", " oXMLDoc.selectSingleNode("descendant::address_component[type='country']/short_name").text
to manually build up an adress based on components provided by the Google Maps geocode API.
Note that if you're parsing things like cities and states out of this, that's a rather silly thing to do, since they're just available in the XML document. You're better of reading them directly from the XML.
Just re-read and it looks like your specific situation is geared to just hospitals so you will not need to consider all of the issues listed here. I'll leave this up, though, in case someone else is looking to parse addresses containing more than just "floor". And still--you could consider the algorithm for finding just the "root".
I worked on a similar project where I needed to identify the "root" physical address and it can be way more complicated than meets the eye. There are SO many pitfalls to watch out for. I ended up having to build a full-on rules engine. Anticipate every possible combination and account for it.
-2 Main St 4th floor
-2 Main St 3rd floor
-2 Main St Unit 3
-4th floor 2 Main St
-Apt 3 2 Main street
-Apt 3 22 Rte 7
-2 Main st 1st floor
...many more
As a general rule you are usually trying to identify a section of the address formatted as "2 Main Street" where you have a number, a street name, and the suffix describing the street/road/drive, etc. Here is a general algorithm which is only the base. You'll need to expand.
If there are any commas, split the string into separate elements to be evaluated individually
Remove all punctuation in the address elements
Find index of the "Street" You'll have to have a fairly extensive list, but here are some:
Road, Rd, Street, St, Boulevard, Blvd, Blv, Way, Avenue, Ave, Kill, Drive, Dr, Lane, Ln, Path, Highway, Hwy, BiWay, Bwy, Expressway. Circle, Cir, Crossing, Xing, Route, Rte, Rural Route, RR
I'm sure you can think of more.
Find the right-most instance of one of those and work backwards from that index until you find a numeric value (or, more accurately, the index of the beginning of a contiguous set of numerical values).
- Make sure that numeric value is not part of the street name (i.e. "3rd Street), which means make sure the numeric value is not followed by a "rd" or "th" or "nd" or make sure it has a space following it. If it is, keep looking back until you find the numeric part of the street address.
- Once you find the numeric value you will likely have what you need. Grab everything between the numeric value and the "Street".
Other things to be careful of:
- Abbreviation for "Street" and for "Saint" are the same. as in "2 St Francis St"
- Abbreviation for "Doctor" and for "Drive" are the same. "3 Dr Jones Dr"
- "Route"s and "Highways" can have numeric values following them as in "2 Route 5"
- Abbreviations for the many incarnations of "Street/Drive" are very often buried in the street name. "3 Caveman Arrival St" contains "ave" and "rr" an "st"
- Numeric portions may also be written as a word as in "Three Main Street",
If you choose to try and identify unwanted sections of address instead of the desired section, you will likewise need to account for a plethora of potential situations:
Apartment, Apt, Suite, Ste, Floor, Fl, Unit, #, Flat, Box, POBox, PO, Building,, Bldg, Bld, Dorm, Room, Rm
Ultimately, you'll likely end up with many scenarios/exceptions you'd need to account for and many "Cases". You might also consider using regular expressions to identify them. Good luck!

Pulling out specific text in an html file using vb.net

I am trying to get three values from a large html file. I thought I could use the substring method, but was informed that the position of the data may change. Basically, in the following code I need to pick out "Total number of records: 106", "Number of records imported:106", and "Number of records rejected: 0"
<B>Total number of records : </B>106</Font><br><Font face="arial" size="2"><B>Number of records imported : </B>106</Font><br><Font face="arial" size="2"><B>Number of records rejected : </B>0</Font>
I hope this is clear enough. Thanks in advance!
Simple string operations like IndexOf() and Substring() should be plenty to do the job. Regular Expressions would be another approach that'd take less code (and may allow more flexibility if the HTML tags can vary), but as Mark Twain would say, I didn't have time for a short solution, so I wrote a long one instead.
In general you'll get better results around here by showing you've at least made a reasonable attempt first and showing where you got stuck. But for this time...here you go. :-)
Private Shared Function GetMatchingCount(allInputText As String, textBefore As String, textAfter As String) As Integer?
'Find the first occurrence of the text before the desired number
Dim startPosition As Integer = allInputText.IndexOf(textBefore)
'If text before was not found, return Nothing
If startPosition < 0 Then Return Nothing
'Move the start position to the end of the text before, rather than the beginning.
startPosition += textBefore.Length
'Find the first occurrence of text after the desired number
Dim endPosition As Integer = allInputText.IndexOf(textAfter, startPosition)
'If text after was not found, return Nothing
If endPosition < 0 Then Return Nothing
'Get the string found at the start and end positions
Dim textFound As String = allInputText.Substring(startPosition, endPosition - startPosition)
'Try converting the string found to an integer
Try
Return CInt(textFound)
Catch ex As Exception
Return Nothing
End Try
End Function
Of course, it'll only work if the text before and after is always the same. If you use that with a driver console app like this (but without the Shared, since it'd be in a Module then)...
Sub Main()
Dim allText As String = "<B>Total number of records : </B>106</Font><br><Font face=""arial"" size=""2""><B>Number of records imported : </B>106</Font><br><Font face=""arial"" size=""2""><B>Number of records rejected : </B>0</Font>"""""
Dim totalRecords As Integer? = GetMatchingCount(allText, "<B>Total number of records : </B>", "<")
Dim recordsImported As Integer? = GetMatchingCount(allText, "<B>Number of records imported : </B>", "<")
Dim recordsRejected As Integer? = GetMatchingCount(allText, "<B>Number of records rejected : </B>", "<")
Console.WriteLine("Total: {0}", totalRecords)
Console.WriteLine("Imported: {0}", recordsImported)
Console.WriteLine("Rejected: {0}", recordsRejected)
Console.ReadKey()
End Sub
...you'll get output like so:
Total: 106
Imported: 106
Rejected: 0

Using split function into array is causing Compile Error: Can't Assign to array

I am attempting to use the split() function to split out a name based on spaces in the given name string. When attempting to compile the code I've written below I get a Compile error: "Can't Assign to array".
I've pretty much copied the microsoft's example from here: https://support.microsoft.com/en-us/kb/266289
Can anyone tell me what I am doing wrong?
Public Function cleanName(position As Integer, inName As String) As String
Dim nameArray() As String
Dim outName As Variant
nameArray = Split(inName, " ")
Select Case position
Case 1 'Titles
outName = nameArray(0)
Case 2 'First Name
outName = nameArray(1)
Case 3 'Middle Name
outName = nameArray(2)
Case 4 'Last Name
outName = nameArray(3)
Case 5 'Suffix
outName = nameArray(4)
Case Else
End Select
cleanName = outName
End Function
I'm coming back to this since I've learn the answer was that my co-worker had written a module called split which kept my previous code from working as it returned a different set of parameters.

Regular Expression Pattern Matching to HTML content

I am trying to do a Regular Expression search on string assigned to the HTML content of web search. The pattern I am trying to match has the following format HQ 12345 the second fragment could also start with a letter so HQ A12345 is also a possibility. As shown in the code below the regex pattern I am using is "HQ .*[0-9]".
Problem is when i run the regex search the pattern matched is not just HQ 959693 but also includes the rest of the html file content as shown in the snapshot of the message box below.
Sub Test()
Dim mystring As String
mystring = getHTMLData("loratadine")
Dim rx As New RegExp
rx.IgnoreCase = True
rx.MultiLine = False
rx.Global = True
rx.Pattern = "HQ .*[0-9]"
Dim mtch As Variant
For Each mtch In rx.Execute(mystring)
Debug.Print mtch
MsgBox(mtch)
Next
End Sub
Public Function getHTMLData (ByVal name As String) As String
Dim XMLhttp: Set XMLhttp = CreateObject("MSXML2.ServerXMLHTTP")
XMLhttp.setTimeouts 2000, 2000, 2000, 2000
XMLhttp.Open "GET", "http://rulings.cbp.gov/results.asp?qu=" & name & "&p=1", False
XMLhttp.send
If XMLhttp.Status = 200 Then
getHTMLData = XMLhttp.responsetext
Else
getHTMLData = ""
End If
End Function
Use ? to specify non-greedy, otherwise the match will consume up until the last digit of the entire string. Also, you are only matching one digit occurrence. Add a + to specify "one or more" so it will match your goal:
HQ .*?[0-9]+
Alternatively, you can try to use a negated character class like so:
HQ [^0-9]*[0-9]+
Or you can even simplify it further:
HQ [^\d]*\d+
Regex matching is by default greedy. Unfortunately I didn't manage to reproduce precisely your issue, but I am pretty sure it is because you a long string which is being matched by '.*' to a number at the end.
I find this link useful, see the explaination near the bottom about the greediness of *
http://www.autohotkey.com/docs/misc/RegEx-QuickRef.htm
I suggest changing your Regex to:
HQ .*?[0-9]+
That will match the "HQ " and any number of characters, followed by any number of numeric characters. It will also consume the minimal amount in the ".*", because of the "?".
Please respond if this does not work and I will getting your Regex running in Excel.