Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

vfp9 get data from google maps 1

Status
Not open for further replies.

stardemos

Technical User
Jan 30, 2017
8
PT
hello, i'm new to vfp,and i'm loving it.
Anyway, i have a questin, i nedd to get the distance from gogle maps, it´s that possible? how?
for better undestanding of the question, this is part of my code.
Code:
Do ydeclare

Local m.yfrom,m.yto
m.yfrom=Inputbox("Origem","",lcTextO)
m.yto=Inputbox("Destino","",m.lctext)

If Empty(m.yfrom) Or Empty(m.yto)
    Return .F.
Else
	m.yfrom=Allt(Strtran(m.yfrom," ","-"))
	m.yto=Allt(Strtran(m.yto," ","-"))
Endi
Local m.url
m.url="[URL unfurl="true"]https://www.google.pt/maps/dir/"+m.yfrom+"/"+m.yto[/URL]
_Cliptext=m.url  &&url in clipboard
Local apie
apie= Createobject("wscript.shell")
apie.Run(m.Url)
Procedure ydeclare
Declare Integer BringWindowToTop In user32 Integer
Declare Integer SetWindowText In user32 Integer,String
Declare Integer Sleep In kernel32 Integer
Endproc

this give me the best ways to get to my clients, now i nedd to get the distance from the google maps. and insert it in my db.
 
since you talked about that,and the machine is a i3 with 4gb of ram with a ssd, and need at least 10 minutes to boot, i have to assume that it's a faulty windows installation, i'm reinstalling everything again.
 
10 minutes boot time sure is a lot. I don't know PHC though. Is that some helath care system? If the system protects personal data and has to be HIPAA compliant, for example, I can't tell it should behave that way, but something security related might be going on, unlocking/decrypting an encrypted subsystem - whatever.

If you don't have VFP at hand it's not really able to give you code you can use.

Bye, Olaf.
 
its a management softaware, to do invoice and stuff like that...

i have vfp installed in another machine, just in case..
 
Obviously you have installation issues you need to resolve before finding your solution.

Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
 
I'd go for the VFPConnection.FLL, then.

Though using Microsoft.XMLHTTP or some equivalent classes on the same line, you can do something like this and use XML parsing and XPath queries (yet another thing to know or learn):
Code:
oHTTP = CreateObject("Microsoft.XMLHTTP")
oHTTP.Open("GET", '[URL unfurl="true"]https://maps.googleapis.com/maps/api/distancematrix/xml?origins=Washington+DC&destinations=Seattle+WA&language=en&units=imperial',[/URL] .F.)
oHTTP.Send()
oXML = oHTTP.responseXML && XMLDOM object
oNodes = oXML.selectNodes("//distance/value") && selectNodes really makes use of MSXML parsing and //distance/value here is just a simple XPath query on the XML DOM.
? oNodes.item(0).text
? val(oNodes.item(0).text)/1609.34 && the distance value XML node always is metric (meters) and this converts to miles

That said, you can read up on the line of classes I mentioned at thread184-1715949 and at thread184-1718701 and most probably somewhere else, too. I still am using the old Microsoft.XMLHTTP, as it translates to different but compatible versions of MSXML depending on the OS and as already said has the most widesrpead support. As you already have experiencesd Microsoft.XMLHTTP roots in MSXML3 and that is the version of the XML libraries in which VFPs XMLTOCURSOR function roots and can't be used without it, so it double points out your system has issues. Are you often "tidying up" your system? My mother learned computers late at the age of about 50, shes 72 today and has become quite en expert on Windows, but also still and always has problem with her system, because of hunting down every warning and error in Windows Event log and other system areas. Your problem may also root in other software installations causing some havoc in the system area, the most important thing to know about the core and basic MSXML libraries is the versions of them are not just normal version numbers of an evolving product getting better, MSXML libraries coexist and have separate, though also overlapping functionalities. MSXML3 is still part of Win10, as is MSXML4, MSXML6 and more. Some library versions are exclusive to Office. I once found a good article on that familiy of MSXML libraries, don't find it right now, but some people, even software developers are sometimes over the top in deinstalling "old" stuff, when there is a newer version.

For one example of why MSXML4 doesn't replace MSXML3: XMLTOCURSOR roots in MSXML3 and XMLAdapter in MSXML4 and MS didn't move XMLTOCURSOR functionality to use MSXML4, maybe for good reasons, eg the XML you generated with CURSORTOXML doesn't parse well in MSXML4 - just one assumption I didn't verify, I don't know any concrete reasons. I may not even need to tell you to keep old stuff, as you use VFP itself, but I just know the type of persons being too half informed to do stuff right. And no, I don't hate my mum.

Above code still works in Windows 10 with MSXML2.XMLHTTP.6.0 in place of Microsoft.XMLHTTP, and most probably will work down to Vista and that'd be suffient platform support anyway. It is not of much value the old Microsoft.XMLHTTP maps to different concrete implementations in different Windows versions down to Windows 98, as support for Windows 98 has not only ended from MS, it's surely only used in ATMs, today (just joking). Anyway, it works and I don't want to risc needing to rewrite some details where I still use it. Habit, perhaps. You may well work with the TRY MSXML2.XMLHTTP.6.0 and CATCH with MSXML2.XMLHTTP.3.0 or even just require MSXML2.XMLHTTP.6.0 today. Still msxml3 missing from your system points out an issue.

Bye, Olaf.
 
Here is an example

Code:
Local lcAddress1, lcAddress2
Local lcURL, loHTTP, lcResult
lcAddress2 = "Grasmarktstraat 100, Brussel"
lcAddress1 = "De Keizerlei 12, Antwerpen"
*!* 56 min, 52,7 km
If !Empty(lcAddress1) Or !Empty(lcAddress2)

	lcURL= "[URL unfurl="true"]http://maps.google.be/maps/api/distancematrix/xml?origins="+lcAddress1+"&destinations="+lcAddress2+"&mode=driving&language=nl-BE&sensor=false"[/URL]

	loHTTP = Createobject("MSXML2.XMLHTTP")
	loHTTP.Open("GET", lcURL, .F.)
	loHTTP.Send
	oXMLDOM = loHTTP.responseXML
	lcResult=(loHTTP.ResponseText)
	oOrigine = oXMLDOM.selectNodes("//origin_address")
	If "NOT_FOUND" $ lcResult Then
		
	Else		&&! NOT_FOUND  = FOUND
		llFound = .T.
	Endif   && "NOT_FOUND" $ lcResult
Endif		&&if !EMPTY(cAddress1)
If llFound
	lcDeparture  = oOrigine.Item(0).Text
	lcArrival0 = oXMLDOM.selectNodes("//destination_address")
	lcArrival = lcArrival0.Item(0).Text
	lcStatus0  = oXMLDOM.selectNodes("//status")
	lcStatus = lcStatus0.Item(0).Text
	lcDuration0 = oXMLDOM.selectNodes("//duration / text")
	lcDuration = lcDuration0.Item(0).Text
	lcDistance0 = oXMLDOM.selectNodes("//distance / text")
	lcDistance = lcDistance0.Item(0).Text
Endif
? lcDeparture
? lcArrival
? lcStatus
? lcDuration
? lcDistance

Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
 
Mike,

Code:
Of course, if it was simply a straight-line distance, you might not need Google maps or any other on-line tool; 
it could be a simple bit of trigonomotry (depending on the country).

It seems that Google is not using a driving distance but using his own straight-line and if using driving it is definitely not using the fastest/shortest track.

Do you have a clue how to implent trigonomotry to calculate a distance?

Regards,

Koen
 
Of course, if it was simply a straight-line distance, you might not need Google maps or any other on-line tool;
it could be a simple bit of trigonomotry (depending on the country).

This is actually quite true, assuming you already have GPS coordinates stored for the two points. I've used it for a website store locator.

You can find the formula all over the net, and yes it's "simple trigonometry". Although I got laughed out of that company's vice president's office for using the phrase "simple trigonometry". (I was DONE with that meeting anyway. [2thumbsup])
 
Well, Jockey, the developer description says google is calculating a distance for a vehicle, more general travel mode, by default a car.
So no, this isn't straight line distance (or curve on the globe). Why would you get no result for a car from NY to Paris, for example?

Code:
Local lcAddress1, lcAddress2
Local lcURL, loHTTP, lcResult
lcAddress2 = "Paris, France"
lcAddress1 = "New York, NY, USA"
If !Empty(lcAddress1) Or !Empty(lcAddress2)

   lcURL= "[URL unfurl="true"]http://maps.google.be/maps/api/distancematrix/xml?origins="+lcAddress1+"&destinations="+lcAddress2+"&mode=driving&language=nl-BE&sensor=false"[/URL]

   loHTTP = Createobject("MSXML2.XMLHTTP")
   loHTTP.Open("GET", lcURL, .F.)
   loHTTP.Send
   oXMLDOM = loHTTP.responseXML
   lcResult=(loHTTP.ResponseText)
   oOrigine = oXMLDOM.selectNodes("//origin_address")
   If "NOT_FOUND" $ lcResult Then
      
   Else      &&! NOT_FOUND  = FOUND
      llFound = .T.
   Endif   && "NOT_FOUND" $ lcResult
Endif      &&if !EMPTY(cAddress1)
If llFound
   lcDeparture  = oOrigine.Item(0).Text
   lcArrival0 = oXMLDOM.selectNodes("//destination_address")
   lcArrival = lcArrival0.Item(0).Text
   lcStatus0  = oXMLDOM.selectNodes("//status")
   lcStatus = lcStatus0.Item(0).Text
   loStatus = oXMLDOM.selectNodes("//row/element/status")
   lcStatus2 = loStatus.Item(0).Text
   If lcStatus2 == "ZERO_RESULTS"
      lcDuration = "no result found" 
      lcDistance = "no result found" 
   Else   
      lcDuration0 = oXMLDOM.selectNodes("//duration / text")
      lcDuration = lcDuration0.Item(0).Text
      lcDistance0 = oXMLDOM.selectNodes("//distance / text")
      lcDistance = lcDistance0.Item(0).Text
   ENDIF 
Endif
? lcDeparture
? lcArrival
? lcStatus
? lcDuration
? lcDistance

Or take Mikes original example and change mode=driving in the URL to mode=bicycling and you get both a different duration (longer) and a different distance (shorter). What contributes of course is, that bicycles can drive on ways cars can't. Still you drive slower. We've been explaining this is not a geolocation and geodistances service already to begin with. I for example said:
myself said:
Default distance is computed for a car, so taking roads into account, and resulting in no result, if you pass in locations parted by an ocean.

If you'd aim for the pure geometric distance, try using this:
Bye, Olaf.
 
>if using driving it is definitely not using the fastest/shortest track.
That can be the case. What I am missing from this obviously is the route the result is based on.
Right now I get different results on the query about the Antwerpen to Brussel distance Mike Gagnon posted, 54.2 km in 47 min. So Google might take current traffic into account.

If that's the case it surely is not the shortest distance route assuming no traffic, it would rather be the fastest, but that's quite useless to know, not knowing which route is that fast in the mind of the google map right now. And it would not at all help in planning a route you want to take tomorrow, in a week or a month. Or use this to solve the travelling salesman optimization.

By the way, I cross checked with the Directions API, the Distance Matrix API has the same travel modes, so these two APIs are most probably closely coupled, the Distance Matrix API just concentrates on the pure duration/distance figure.

Bye, Olaf.

 
It seems that Google is not using a driving distance but using his own straight-line and if using driving it is definitely not using the fastest/shortest track.

If you think that's bad, try asking Google for cycling directions. Frequently it will send you along busy main roads or even motorways, ignoring nearby off-road bike paths - at least, that's the case where I live (and I have considerable experience of this).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Using the directions API, it will be possible to follow the steps and decisions taken by the Google service.

With exception handling close to zero:

Code:
#DEFINE TEST_CLASS	.T.

#IF TEST_CLASS

CLEAR

LOCAL Traject AS RoutePlanner
LOCAL TrajectStep AS RP_Step
LOCAL Distance AS Integer
LOCAL Duration AS Integer

m.Traject = CREATEOBJECT("RoutePlanner")

* IF m.Traject.GetRoute("Jardim Botânico, Coimbra", "Jardin des Plantes, Paris")

IF m.Traject.GetRoute("Grasmarktstraat 100, Brussel", "De Keizerlei 12, Antwerpen")
	m.Distance = 0
	m.Duration = 0
	FOR EACH TrajectStep IN m.Traject.Steps
		m.Distance = m.Distance + m.TrajectStep.Distance
		m.Duration = m.Duration + m.TrajectStep.Duration
		? m.TrajectStep.DurationText, "/", m.TrajectStep.DistanceText, "/", m.TrajectStep.Instructions
	ENDFOR
	? "Total distance (meters):", m.Distance
	? "Total duration (seconds):", m.Duration
ENDIF

#ENDIF

DEFINE CLASS RoutePlanner AS Custom

	Language = "en-US"
	FromAddress = ""
	ToAddress = ""
	TravelMode = "DRIVING"
	Steps = .NULL.
	Service = .NULL.

	FUNCTION Init
	
		This.Service = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
		RETURN !ISNULL(This.Service)
	
	ENDFUNC
	
	FUNCTION Destroy
	
		This.Clear()
		This.Service = .NULL.
	
	ENDFUNC
	
	FUNCTION GetRoute (FromAddress AS String, ToAddress AS String) AS Boolean
	
		LOCAL URL AS String
		LOCAL Response AS MSXML2.DOMDocument60
		LOCAL Nodes AS MSXML2.IXMLDOMNodeList, Children AS MSXML2.IXMLDOMNodeList
		LOCAL Node AS MSXML2.IXMLDOMNode
		LOCAL SingleStep AS RP_Step
		LOCAL DecimalPoint AS Character
		
		This.FromAddress = m.FromAddress
		This.ToAddress = m.ToAddress
		This.Clear()

		m.URL = "[URL unfurl="true"]https://maps.google.com/maps/api/directions/xml?origin="[/URL] + This.EncodeURL(m.FromAddress) + ;
					"&" + "destination=" + This.EncodeURL(m.ToAddress) + ;
					"&" + "mode=" + This.TravelMode + ;
					"&" + "language=" + This.Language + "&" + "sensor=false"
		
		WITH This.Service AS MSXML2.ServerXMLHTTP60
		
			.open("GET", m.URL, .F.)
			.send()
			m.Response = .responseXML
		
		ENDWITH
		
		m.Nodes = m.Response.selectNodes("/DirectionsResponse/status")
		IF ISNULL(m.Nodes) OR m.Nodes.length = 0 OR m.Nodes.item(0).text != "OK"
			RETURN .F.
		ENDIF
		
		This.Steps = CREATEOBJECT("Collection")
		m.DecimalPoint = SET("Point")
		SET POINT TO "."
		
		m.Nodes = m.Response.selectNodes("/DirectionsResponse/route/leg/step")
		
		FOR EACH m.Node IN m.Nodes
		
			m.NewStep = CREATEOBJECT("RP_Step")
			
			WITH m.NewStep AS RP_Step
			
				.TravelMode = m.Node.selectNodes("travel_mode").item(0).text
				WITH .StartLocation AS RP_Location
					.Latitude = VAL(m.Node.selectNodes("start_location/lat").item(0).text)
					.Longitude = VAL(m.Node.selectNodes("start_location/lng").item(0).text)
				ENDWITH
				WITH .EndLocation AS RP_Location
					.Latitude = VAL(m.Node.selectNodes("end_location/lat").item(0).text)
					.Longitude = VAL(m.Node.selectNodes("end_location/lng").item(0).text)
				ENDWITH
				.Instructions = This.RemoveMarkup(m.Node.selectNodes("html_instructions").item(0).text)
				.Duration = VAL(m.Node.selectNodes("duration/value").item(0).text)
				.DurationText = m.Node.selectNodes("duration/text").item(0).text
				.Distance = VAL(m.Node.selectNodes("distance/value").item(0).text)
				.DistanceText = m.Node.selectNodes("distance/text").item(0).text
				m.Children = m.Node.selectNodes("manouever")
				IF !ISNULL(m.Children) AND m.Children.length > 0
					.Manouever = m.Children.item(0).text
				ENDIF
			
			ENDWITH
			
			This.Steps.Add(m.NewStep)
		ENDFOR
		
		SET POINT TO m.DecimalPoint

		RETURN .T.
	
	ENDFUNC
	
	PROCEDURE Clear
		
		IF !ISNULL(This.Steps)
			This.Steps.Remove(-1)
		ENDIF
		
		This.Steps = .NULL.
	ENDPROC					

	FUNCTION EncodeURL (Source AS String) AS String
	
		LOCAL CharIndex AS Integer
		LOCAL SingleChar AS Character
		LOCAL Encoded AS String
		LOCAL UTF8 AS String
		
		m.UTF8 = STRCONV(STRCONV(m.Source,1),9)
		m.Encoded = ""
		FOR m.CharIndex = 1 TO LEN(m.UTF8)
			m.SingleChar = SUBSTR(m.UTF8, m.CharIndex, 1)
			IF m.SingleChar $ "&=%:/+ " OR ASC(m.SingleChar) > 127 OR ASC(m.SingleChar) < 32
				m.SingleChar = "%" + RIGHT(TRANSFORM(ASC(m.SingleChar),"@0"), 2)
			ENDIF
			m.Encoded = m.Encoded + m.SingleChar
		ENDFOR
		
		RETURN m.Encoded
	
	ENDFUNC
	
	FUNCTION RemoveMarkup (Source AS String) AS String
	
		LOCAL TaggedElement AS String
		LOCAL Cleared AS String
		
		m.Cleared = m.Source
		m.TaggedElement = STREXTRACT(m.Cleared, "<", ">", 1, 4)
		DO WHILE !EMPTY(m.TaggedElement)
			m.Cleared = STRTRAN(m.Cleared, m.TaggedElement, "")
			m.TaggedElement = STREXTRACT(m.Cleared, "<", ">", 1, 4)
		ENDDO
		
		RETURN m.Cleared
	
	ENDFUNC
			
ENDDEFINE

DEFINE CLASS RP_Step AS Custom

	TravelMode = ""
	StartLocation = .NULL.
	EndLocation = .NULL.
	Instructions = ""
	Duration = 0
	DurationText = ""
	Distance = 0
	DistanceText = ""
	Manouever = ""
	
	FUNCTION Init
	
		This.StartLocation = CREATEOBJECT("RP_Location")
		This.EndLocation = CREATEOBJECT("RP_Location")
	
	ENDFUNC

	FUNCTION Destroy
	
		This.StartLocation = .NULL.
		This.EndLocation = .NULL.
	
	ENDFUNC

ENDDEFINE

DEFINE CLASS RP_Location AS Custom

	Latitude = 0
	Longitude = 0

ENDDEFINE
 
Code:
If !Empty(lcAddress1) Or !Empty(lcAddress2)

* I think, you mean
If !( Empty(lcAddress1) Or Empty(lcAddress2) )
 
Hi,
The coding provided by Mike Gagnon here works like a charm.
I faced a difficulty: different values using Mike's code and an routedescription via GoogleMaps in my Internet browse. This was due to the fact Google is soo smart to take into account of an registered roadblock, read accident, on the fastest route and thus showing a longer, but faster alternative.
Thanks.
Jockey
 
>The coding provided by Mike Gagnon here works like a charm.

If you try Paris to NY you get no results, the XML will not work out correctly without my modification taking ZERO_RESULTS into account.

Bye, Olaf.
 
Olaf,
driving from Paris to NewYork and you get no results? Does not sound like a bug to me.
You misunderstood my remark,
the distance showed, driving from Antwerpen to Brussels showed a different value with the Mike's code and the results you would get by invoking Google Maps actively with your internet browser.
There was a road-block on the Highway so the next alternative road was presented.
With no roadblock or other traffic obstacles they gave both the same results.
The addition of an error catch, as proposed by you, If lcStatus2 == "ZERO_RESULTS", is advisable. I suppose more error catches need to be added to this very basic routine.

Regards,

Jockey

 
No, google is not faulty, but Mike Gagnons script does not take the secondary status ZERO_RESULTS into account.
Just do and you'll see, google will response with an OK status for the query itself and a ZERO_RESULTS status of the distance/durations.

mgagnon_error_s9dtzp.png


The reason is simply two statuses:

XML:
<?xml version="1.0" encoding="UTF-8"?>
<DistanceMatrixResponse>
 [highlight #FCE94F]<status>OK</status>[/highlight]
 <origin_address>New York City, New York, Verenigde Staten</origin_address>
 <destination_address>Parijs, Frankrijk</destination_address>
 <row>
  <element>
   [highlight #FCE94F]<status>ZERO_RESULTS</status>[/highlight]
  </element>
 </row>
</DistanceMatrixResponse>

And I now say for the third time, that my version of Mikes script fixes that, by specifically also extracting the row/element/status.
Simply please look back a few posts.

Lately it seems the attention span of many people here is very short.

Bye, Olaf.
 
The error checking is not in place. Olaf's version is more coplete.
I should also check for other statuses

Code:
case inlist(m.ixnStatus.text,'ZERO_RESULTS','OVER_QUERY_LIMIT',;
    'REQUEST_DENIED','INVALID_REQUEST','UNKNOWN_ERROR')

Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
 
If sample code would always be accompanied with all necessary error handling the core code of interest would often become less prominent and clear, so that was never my criticism.

Also, yes, there are even more query and row result states to consider. Since you may pass in multiple origins and destinations, I also would expect more rows in the result are possible, once you use the full request capabilities, but you may restrict your own usage in that respect and prevent that problem this way.

In the end it's just a heads up on this aspect, the code as is can work ok for routing of routes you know are not problematic.

Last not least I made the Paris<->NY request merely to show this API is not about the geometric distance. There would be no problem returning the geometric distance for any two points on earth, as Dan said that's simple (enough) trigonometry.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top