ooRexx logo
/* ---------------------------------------------------------------- */
/* Two classes to facilitate Google maps interaction:               */
/*                                                                  */
/*   GeoLoc - an immutable definition of a latitude/longitude pair  */
/*   GeoPath - a list of GeoLoc objects defining a route or path    */
/*                                                                  */
/* ---------------------------------------------------------------- */
/*                                                                  */
/* Originally by Ruurd J. Idenburg                                  */
/*                                                                  */
/* No copyright, no licence, no guarantees or warrantees, be it     */
/* explicit, implicit or whatever. Usage is totally and completely  */
/* at the users own risk, the author shall not be liable for any    */
/* damages whatsoever, for any reason whatsoever.                   */
/*                                                                  */
/* Please keep this comment block intact when modifying this code   */
/* and add a note with date and a description.                      */
/*                                                                  */
/* ---------------------------------------------------------------- */
/* 2013/12/01 - Initial version approximately                       */
/* 2020/02/15 - Corrected comments above                            */
/* ---------------------------------------------------------------- */

::class geoloc public
-- GeoLoc class, for now consisting of an immutable latitude
-- and longitude specification in decimal degrees.
--
-- The precision for calculations is a settable attribute, initially
-- set to the highest precision supported by the 'rxmath' library.
--

::attribute latitude get
::attribute longitude get
::attribute precision

-- (equatorial) radius of the Earth in meters as used by Google
::constant earthRadius 6378137

::method init
        expose latitude longitude precision
        use strict arg lat,lon
-- set precision to the maximum supported number of digits in the rxmath library
        precision = 16
-- create proper and valid latitude and longitude  
        latitude = .latitude~new(lat)
        longitude = .longitude~new(lon)

::method distanceFrom
-- Calculates the distance between the receiving and the argument (geo)location
-- in thousands of Kilometers or Miles, as specified by the optional second argument.
-- Default is the the metric system, specify 'M' for the imperial system.
--
        numeric digits (self~precision)
-- distance in Kilometers(K), is default, or Miles(M)
        use strict arg fromGeoLoc, unit='K'    
        if fromGeoLoc~class<>.geoloc then raise syntax 88.914 array("1-(geoloc)",self~class~id)
        unit = unit~subchar(1)~upper
-- distance will be calculated in thousands of the unit, be it metric or imperial
        if ('KM')~pos(unit)==0 then raise syntax 88.916 array("2-(unit)",'"K(m)","M(ile)"',unit)
        unit = '1000 1639'~word(('KM')~pos(unit))
-- I'm told that the best formula for short distances is:
-- d=R*2*asin(sqrt((sin((lat1-lat2)/2))^2 + cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2)^2)))
        lat1 = self~latitude
        lat2 = fromGeoLoc~latitude
        lon1 = self~longitude
        lon2 = fromGeoLoc~longitude
        R = self~earthRadius
        dist = R*2*asin(sqrt((sin(lat1-lat2)/2)**2 + cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2)**2)),,'R')
--for a possibly future method implementation
--R*2*(((lat1-lat2)/2)~sin**2 + lat1~cos*lat2~cos*(((lon1-lon2)/2)**2)~sqrt)~asin(,'R')
--
-- return distance in thousands of unit
        return (dist/unit)~format(,3)

::method distanceTo
        forward message('distanceFrom') to(self)

::class geoPath public subclass list
-- GeoPath class, defined as a list of GeoLocs.
-- Class method 'of' in the .list class uses insert instance methods, no need to override.

::method insert
        use arg item
        if item~class<>.geoloc then do
                raise syntax 88.914 array(1,.geoloc~id)
        end
-- 2nd argument is index if specified
        if arg(2,'E') then do
                return self~insert:super(item,arg(2))
        end
-- no 2nd argument means append
        else do
                return self~insert:super(item)
        end
exit
       
::method append
        use strict arg item
        self~insert(item)
exit
       
::method put
-- same as insert with 2nd argument
        use strict arg item, index
        self~insert(item,index)
       
::method distance
        use strict arg unit='K'
        items = self~items
        distance = 0
        do l=0 to items-2
    distance += self[l]~distanceTo(self[l+1],unit)
        end
  return distance
       
::class number public subclass string
-- Number class, a subclass of the String class
-- Values can be any valid number
--
-- All arithmetic methods need to be defined here, because
-- the result of the operation should be another object of
-- the Number class, and the result of the String class operators
-- always is a String class object.
--
-- Note that subclasses of the Number class will yield results
-- that belong to that particular receiver subclass.

::method init
        self~init:super
        if (self)~datatype\='NUM' then do       --if self is not a number
                raise syntax 93.904 array(1,self) -- then raise an error and quit      
        end

::method '+'
--use strict arg num
--return self~class~new(self~'+':super(num))
-- or:
        forward class(super) continue
        return self~class~new(result)
       
::method '-'
        use strict arg num
        return self~class~new(self~'-':super(num))

::method '*'
        use strict arg num
        return self~class~new(self~'*':super(num))

::method '/'
        use strict arg num
        return self~class~new(self~'/':super(num))

::method '%'
        use strict arg num
        return self~class~new(self~'%':super(num))

::method '//'
        use strict arg num
        return self~class~new(self~'//':super(num))

::method '**'
        use strict arg num
        return self~class~new(self~'**':super(num))

::class latitude public subclass number
-- Latitude class, a subclass of the Number class
-- Values range from -90 thru +90

::method init
-- the number class will check if I'm a valid number
        self~init:super  
-- I check if I'm a valid latitude
        if self<-90 | self>90 then do
                raise syntax 88.907 array("1-(latitude)","-90","90",self)
        end

::class longitude public subclass number
-- Longitude class, a subclass of the Number class
-- Values range from -180 thru +180

::method init
        self~init:super
        if self<-180 | self>180 then do
                raise syntax 88.907 array("1-(longitude)","-180","180",self)
        end

-- Simplify function names for the necessary 'RxMath' functions
::routine Sin EXTERNAL "LIBRARY rxmath RxCalcSin"
::routine Cos EXTERNAL "LIBRARY rxmath RxCalcCos"
::routine Asin EXTERNAL "LIBRARY rxmath RxCalcArcSin"
::routine Sqrt EXTERNAL "LIBRARY rxmath RxCalcSqrt"

-- number.cls added above
--::requires "number.cls"
 
If you feel inclined to make corrections, suggestions etc., please mail me any.
All content © Ruurd Idenburg, 2007–, except where marked otherwise. All rights reserved. This page is primarily for non-commercial use only. The Idenburg website records no personal information and sets no ‘cookies’. This site is hosted on a VPS(Virtual Private System) rented from Transip.nl, a Dutch company, falling under Dutch (privacy) laws (I think).

This page updated on by Ruurd Idenburg.