Program: Geocode
//*******************************************************************************************
// @Descr: This program receives an address from an external program. That address is then
// sent to googlemaps where the latitude and longitude are returned to this program
// in an xml stream. This program then returns the latitude and longitude to the
// calling program.
//
// The google service is accessed with a URL string, not with an xml stream. It
// should also be noted that google requires that google maps be used to display
// any output and that the application be accessible to the public. See googles
// full terms at http://code.google.com/apis/maps/terms.html#section_10_12
// The URL for googles geocoding page is:
// http://code.google.com/apis/maps/documentation/geocoding/index.html
// And finally, the URL for google maps home page is:
// http://code.google.com/apis/maps/
// @Notes:
//*******************************************************************************************
H dftactgrp(*no) bnddir('RXSBND')
/copy rxs,RXSCp
D getcoords PR
D #Address 256a
D #longitude 15a
D #latitude 15a
D #accuracy 1a
D #error 256a
D getcoords pi
D @Address 256a
D @longitude 15a
D @latitude 15a
D @accuracy 1a
D @error 256a
D allHandler pr
D pType value like(RXS_Type)
D pXPath value like(RXS_XPath)
D pData value like(RXS_XmlData)
D pDataLen value like(RXS_Length)
D errHandler pr
D pCurLine 10i 0 value
D pCurCol 10i 0 value
D pErrStr 1024a value varying
d gPoint s 30a
d gStrPos s 2 0
d gEndPos s 2 0
D gError ds likeds(RXS_Error)
D ginCfg ds likeds(RXS_GetUriIn) inz
D grspData s like(RXS_XmlData)
D grspHttpHdr s like(RXS_XmlData)
/free
monitor;
// Send request to google (using a URL string)
ginCfg.URI ='http://maps.google.com/maps/geo?' +
'q=' + %trim(@address) +
'&output=xml&oe=utf8&sensor=false';
gInCfg.Port = 80;
RXS_getUri(gInCfg: *omit: grspData: gRspHttpHdr);
exsr parse;
exsr getPoint;
on-error;
gError = RXS_catchError();
@Error = gerror;
endmon;
*inlr = *on;
//-----------------------------------------------------------------------
// Setup event handlers that will get called each time an element content
// or attribute is encountered. Then call the parser with the xml
// received in on the request.
//-----------------------------------------------------------------------
begsr parse;
RXS_allElemContentHandler(%paddr(allHandler));
RXS_allAttrHandler(%paddr(allHandler));
RXS_parse(grspData: RXS_VAR: %paddr(errHandler));
endsr;
//-----------------------------------------------------------------------
// Get the longitude and latitude out of the gpoint variable
//-----------------------------------------------------------------------
begsr GetPoint;
gStrPos = 1;
gEndPos = %scan(',': gpoint:gStrPos);
@longitude = %subst(gpoint:gStrPos:gEndPos-1);
gStrPos = gEndPos +1;
gEndPos = %scan(',': gpoint: gStrPos);
@latitude = %subst(gpoint:gStrPos:(gEndPos-gStrPos));
endsr;
/end-free
//--------------------------------------------------------------------------------------------
// @Desc: Handle all events based on specifying RXS_allElemContentHandler.
// Note that instead of displaying pData's contents in the job log
// one could just as easily place that data in a physical file or
// display it back to an interactive green screen display.
//
// @Notes: There are four events that your program can be notified of through the allHandler
// sub procedure and they are passed in the pEvntType parm. An event is triggered
// as the parser reads the document top down, left to right (same way you read the
// newspaper). Note that you will only be notified of events you specified before the
// call to RXS_parse by using API's RXS_allElemBegHandler, RXS_allElemContentHandler,
// RXS_allElemEndHandler, and RXS_allAttrHandler.
//
// The four events and their values (note that these can all be overidden by
// changing the RXSCFG file or by doing a temporary override on the RXS_parse command)
//
// Element Begin = '>' -- Placed at end of string (i.e. '/elem>')
// Element Content = '/' -- Placed at end of string (i.e. '/elem/')
// Element End = '/>' -- Placed at end of string (i.e. '/elem/>')
// Attribute = '@' -- Placed between the elem and attr (i.e. '/elem@attr')
//
// The most common events you will use are Element Content and Attribute simply
// because these are the two that provide business data via the pData parm on the
// allHandler procedure interface. Events Element Begin and Element End are very
// helpful when you have repeating XML data. You can then use the Begin and End
// events to do a CLEAR and WRITE to a PF record respectively. Remember that
// in-between the Element Begin and Element End events you will be notified of all the
// element content, which means that after the CLEAR (i.e. Element Begin event) you
// can place data into the PF record until you reach the Element End event at which
// time you can do a WRITE of that record because it has now been populated with data.
//--------------------------------------------------------------------------------------------
P allHandler b
D allHandler pi
D pType value like(RXS_Type)
D pXPath value like(RXS_XPath)
D pData value like(RXS_XmlData)
D pDataLen value like(RXS_Length)
/free
select;
when pXPath = '/kml/Response/name/';
@Address = pData;
when pXPath = '/kml/Response/Status/code/';
@error = pData;
when pXPath = '/kml/Response/Placemark/AddressDetails@Accuracy';
@Accuracy = pData;
when pXPath = '/kml/Response/Placemark/address/';
@Address = pData;
when pXPath = '/kml/Response/Placemark/Point/coordinates/';
gPoint = pData;
endsl;
/end-free
P e
//--------------------------------------------------------------------------------------------
// @Desc: If an error occurs this local sub procedure will be called by the parser.
//--------------------------------------------------------------------------------------------
P errHandler B
D errHandler PI
D pCurLine 10i 0 value
D pCurCol 10i 0 value
D pErrStr 1024a value varying
/free
gError.code = 'GETCOORDS.1';
gError.severity = 100;
gError.pgm = 'GETCOORDS.errHandler';
gError.text =
'Line:' + %char(pCurLine) +
' Column:' + %char(pCurCol) +
' ' + pErrStr;
/end-free
P E
Program: GetCoords
//*******************************************************************************************
// @Descr: Simple program that passes a hard-coded address to a program that
// returns latitude and longitude, which are then written to the joblog.
// If the address could not be processed an error message is written to the
// joblog instead.
// @Notes:
//*******************************************************************************************
H dftactgrp(*no) bnddir('RXSBND') OPTION(*NODEBUGIO)
/copy rxs,RXSCp
D getcoords PR ExtPgm('GETCOORDS')
D #Address 256a
D #longitude 15a
D #latitude 15a
D #accuracy 1a
D #error 256a
d formatAddress PR 256a
d pAddress 256a value
D fndRpl pr
D pString 65535a varying
D pFrom 10a value varying
D pTo 10a value varying
d gAddress s 256a
d gLongitude s 15a
d gLatitude s 15a
d gAccuracy s 1a
d gError s 256a
/free
// We load the address to look up below. Google requires that the address
// be passed in with no blanks, all blanks in the address are to be replaced
// with plus signs. So I call a subprocedure that replaces the imbedded
// blanks in the address with plus signs.
gAddress = '10 civic center plaza, mankato, mn';
gAddress = formatAddress(gAddress);
clear gLongitude;
clear gLatitude;
clear gAccuracy;
clear gError;
getcoords(gAddress: gLongitude: gLatitude: gAccuracy: gError);
if gError = '200';
// success
RXS_log(RXS_INFO: %trim(gAddress));
RXS_log(RXS_INFO: 'Latitude = ' + gLatitude);
RXS_log(RXS_INFO: 'Longitude= ' + gLongitude);
else;
// error
RXS_log(RXS_INFO: 'Coordinates could not be retrieved.');
ENDIF;
*inlr = *on;
Return;
/end-free
*===================================================================
* Replace any blanks in the address with plus signs
*===================================================================
P formatAddress B
d formatAddress pi 256a
d paddress 256a value
D from s 10a dim(10) varying
D to s 10a dim(10) varying
D str s 65535a varying
/free
// we trim pAddress before doing the find/replace to remove any
// leading or trailing blanks. Since str is a varying length
// field, when we trim pAddress into it, str will only be as
// long as the address we want to process.
str = %trim(pAddress);
fndRpl(str: ' ': '+');
pAddress = str;
return pAddress;
/end-free
P E
*===================================================================
* Generic Scan/Replace routine
*===================================================================
P fndRpl b
D fndRpl pi
D pString 65535a varying
D pFrom 10a value varying
D pTo 10a value varying
D pos s 10i 0
D newStr s 256a varying
D toLen s 10i 0
/free
newStr = pString;
pos = %scan(pFrom: newStr);
dow pos > *zero;
newStr = %replace(pTo: newStr: pos: %len(pFrom));
toLen = %len(pTo);
if pos + toLen <= %len(newStr);
pos = %scan(pFrom: newStr: pos + toLen);
else;
leave;
endif;
enddo;
pString = newStr;
/end-free
P e
Sample output:
10 Civic Center Plaza, Mankato, MN 56001, USA
Latitude = 44.1640494
Longitude= -94.0050065