module AdiwgCoordinates

History:

Stan Smith 2017-08-23 added is_polygon_clockwise method
Stan Smith 2017-05-24 added checkGeometry method to move objects to real world
Stan Smith 2016-11-10 added computedBbox computation
Stan Smith 2015-07-16 moved module_coordinates from mdJson reader to internal
Stan Smith 2015-07-14 refactored to remove global namespace constants
Stan Smith 2015-06-22 replace global ($response) with passed in object (responseObj)
Stan Smith 2014-12-15 refactored to handle namespacing readers and writers
Stan Smith 2014-05-23 added getLevels
     Stan Smith 2013-11-13 added getDimension
     Stan Smith 2013-11-07 original script

Public Class Methods

addPoint(aPoint, aNorthings, aEastings) click to toggle source

add to tuple to an array

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 221
def self.addPoint(aPoint, aNorthings, aEastings)
   aEastings << aPoint[0]
   aNorthings << aPoint[1]
end
checkGeometry(aCoords, aNorthings, aEastings) click to toggle source

move geometry to real world if possible move geometry to a single spanned meridian

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 169
def self.checkGeometry(aCoords, aNorthings, aEastings)

   aGeoNorthings = []
   aGeoEastings = []

   unpackCoords(aCoords, aGeoNorthings, aGeoEastings)

   minEast = aGeoEastings.min
   maxEast = aGeoEastings.max

   # if all coordinates in -1 or +1 world, move back to real world
   if maxEast < -180
      aGeoEastings.collect! { |x| x + 360 }
   end
   if minEast > 180
      aGeoEastings.collect! { |x| x - 360 }
   end

   minEast = aGeoEastings.min
   maxEast = aGeoEastings.max

   # if geoObject spans a meridian, find out which one and make it default
   if @spanWorld == 0
      @spanWorld = -1 if maxEast < 0 && minEast < -180
      @spanWorld = 1 if minEast > 0 && maxEast > 180
   end

   # bring -1 and +1 world objects back into default meridian world
   if @spanWorld == -1 && maxEast > 180
      aGeoEastings.collect! { |x| x - 360 }
   end
   if @spanWorld == 1 && minEast < -180
      aGeoEastings.collect! { |x| x + 360 }
   end

   aEastings.concat(aGeoEastings)
   aNorthings.concat(aGeoNorthings)

end
computeBbox(aGeo) click to toggle source

compute bounding box

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 67
def self.computeBbox(aGeo)

   # reset spanned world to 0 before computing bounding box
   @spanWorld = 0

   # find all the eastings (x) and northings (y) in the GeoJSON object
   aNorthings = []
   aEastings = []
   aGeo.each do |hGeo|
      unpackGeometry(hGeo, aNorthings, aEastings)
   end

   # return if no coordinates found in geographic object(s)
   return {} if aNorthings.empty? || aEastings.empty?

   # find most north/south points
   north = aNorthings.max
   south = aNorthings.min

   # find most east/west spanning the meridian
   eastM = aEastings.max
   westM = aEastings.min

   # find most east/west spanning the anti-meridian
   aPositiveEast = []
   aNegativeEast = []
   aEastings.each do |east|
      aNegativeEast << east if east < 0.0
      aPositiveEast << east if east >= 0.0
   end
   aNegativeEast.uniq!
   aPositiveEast.uniq!

   eastAM = aNegativeEast.max
   westAM = aPositiveEast.min

   # if eastings are all positive or all negative hemisphere is decided
   if aPositiveEast.empty? || aNegativeEast.empty?
      east = eastM
      west = westM
   else
      # choose which meridian to span based on smaller edge-to-edge distance
      distanceM = eastM - westM
      distanceAM = (180 + eastAM) + (180 - westAM)
      if distanceM.abs <= distanceAM.abs
         # this spanned to  meridian
         east = eastM
         west = westM
      else
         # this spanned tne anti-meridian
         east = eastAM
         west = westAM
      end
   end

   bBox = {}
   bBox[:westLongitude] = west
   bBox[:eastLongitude] = east
   bBox[:southLatitude] = south
   bBox[:northLatitude] = north

   return bBox

end
getDimension(aCoords) click to toggle source

get the number of dimensions in a coordinate array

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 43
def self.getDimension(aCoords)

   if aCoords[0].kind_of?(Array)
      coordDim = getDimension(aCoords[0])
   else
      coordDim = aCoords.length
   end

   return coordDim

end
getLevels(aCoords) click to toggle source

get the number of levels in the coordinate array

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 56
def self.getLevels(aCoords)

   i = 1
   if aCoords[0].kind_of?(Array)
      i = i + getLevels(aCoords[0])
   end
   return i

end
is_polygon_clockwise(aCoords) click to toggle source
# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 226
def self.is_polygon_clockwise(aCoords)
   area = 0.0
   i = 0
   n = aCoords.length - 1
   until i == n
      area += (aCoords[i][0] * aCoords[i+1][1]) - (aCoords[i][1] * aCoords[i+1][0])
      i += 1
   end
   area += (aCoords[n][0] * aCoords[0][1]) - (aCoords[n][1] * aCoords[0][0])
   return area >= 0 ? false : true
end
stringifyCoords(aCoords, responseObj) click to toggle source

repack coordinate array into single string for ISO

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 20
def self.stringifyCoords(aCoords, responseObj)

   s = ''
   i = 0
   coordCount = aCoords.length
   aCoords.each do |coord|
      if coord.kind_of?(Array)
         s = s + unpack(coord, responseObj)
      else
         i += 1
         s = s + coord.to_s
         if i < coordCount
            s = s + ','
         end
         s = s + ' '
      end
   end

   return s

end
unpackCoords(aCoords, aNorthings, aEastings) click to toggle source

unpack coordinate arrays

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 210
def self.unpackCoords(aCoords, aNorthings, aEastings)
   if aCoords[0].kind_of?(Array)
      aCoords.each do |aTuple|
         unpackCoords(aTuple, aNorthings, aEastings)
      end
   else
      addPoint(aCoords, aNorthings, aEastings)
   end
end
unpackGeometry(hGeo, aNorthings, aEastings) click to toggle source

compute bounding box

# File lib/adiwg/mdtranslator/internal/module_coordinates.rb, line 133
def self.unpackGeometry(hGeo, aNorthings, aEastings)

   # geometry objects
   type = hGeo[:type]
   if %w{ Point LineString Polygon MultiPoint MultiLineString MultiPolygon }.one? {|word| word == type}
      checkGeometry(hGeo[:coordinates], aNorthings, aEastings)
   end

   # geometry collections
   if hGeo[:type] == 'GeometryCollection'
      hGeo[:geometryObjects].each do |aGeoObj|
         checkGeometry(aGeoObj[:coordinates], aNorthings, aEastings)
      end
   end

   # features
   if hGeo[:type] == 'Feature'
      featureType = hGeo[:geometryObject][:type]
      if featureType == 'GeometryCollection'
         unpackGeometry(hGeo[:geometryObject], aNorthings, aEastings)
      else
         checkGeometry(hGeo[:geometryObject][:coordinates], aNorthings, aEastings)
      end
   end

   # feature collections
   if hGeo[:type] == 'FeatureCollection'
      hGeo[:features].each do |hFeature|
         unpackGeometry(hFeature, aNorthings, aEastings)
      end
   end

end