Fix geo areas computations
This commit is contained in:
parent
569433bacb
commit
4dfc1c719f
4 changed files with 75 additions and 7 deletions
|
@ -78,8 +78,8 @@ class GeoArea < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def area
|
def area
|
||||||
if polygon? && RGeo::Geos.supported?
|
if polygon?
|
||||||
rgeo_geometry&.area&.round(1)
|
GeojsonService.area(geometry.deep_symbolize_keys).round(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,74 @@ class GeojsonService
|
||||||
|
|
||||||
polygon.to_json
|
polygon.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The following code is ported from turfjs
|
||||||
|
# https://github.com/Turfjs/turf/blob/master/packages/turf-area/index.ts
|
||||||
|
|
||||||
|
EQUATORIAL_RADIUS = 6378137
|
||||||
|
|
||||||
|
def self.area(geojson)
|
||||||
|
calculate_area(geojson)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.calculate_area(geom)
|
||||||
|
total = 0
|
||||||
|
case geom[:type]
|
||||||
|
when 'Polygon'
|
||||||
|
polygon_area(geom[:coordinates]);
|
||||||
|
when 'MultiPolygon'
|
||||||
|
geom[:coordinates].each do |coordinates|
|
||||||
|
total += polygon_area(coordinates)
|
||||||
|
end
|
||||||
|
total
|
||||||
|
else
|
||||||
|
total
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.polygon_area(coordinates)
|
||||||
|
total = 0
|
||||||
|
if coordinates.present?
|
||||||
|
coordinates = coordinates.dup
|
||||||
|
total += ring_area(coordinates.shift).abs
|
||||||
|
coordinates.each do |coordinates|
|
||||||
|
total -= ring_area(coordinates).abs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
total
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ring_area(coordinates)
|
||||||
|
total = 0
|
||||||
|
coords_length = coordinates.size
|
||||||
|
|
||||||
|
if coords_length > 2
|
||||||
|
coords_length.times do |i|
|
||||||
|
if i == coords_length - 2 # i = N-2
|
||||||
|
lower_index = coords_length - 2
|
||||||
|
middle_index = coords_length - 1
|
||||||
|
upper_index = 0
|
||||||
|
elsif i == coords_length - 1 # i = N-1
|
||||||
|
lower_index = coords_length - 1
|
||||||
|
middle_index = 0
|
||||||
|
upper_index = 1
|
||||||
|
else # i = 0 to N-3
|
||||||
|
lower_index = i
|
||||||
|
middle_index = i + 1
|
||||||
|
upper_index = i + 2
|
||||||
|
end
|
||||||
|
p1 = coordinates[lower_index]
|
||||||
|
p2 = coordinates[middle_index]
|
||||||
|
p3 = coordinates[upper_index]
|
||||||
|
total += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]))
|
||||||
|
end
|
||||||
|
total = total * EQUATORIAL_RADIUS * EQUATORIAL_RADIUS / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
total
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rad(num)
|
||||||
|
num * Math::PI / 180
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1305,7 +1305,7 @@ describe Dossier do
|
||||||
'type' => 'Polygon'
|
'type' => 'Polygon'
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
area: 219.0,
|
area: 103.6,
|
||||||
champ_id: champ_carte.stable_id,
|
champ_id: champ_carte.stable_id,
|
||||||
dossier_id: dossier.id,
|
dossier_id: dossier.id,
|
||||||
id: geo_area.id,
|
id: geo_area.id,
|
||||||
|
|
|
@ -2,15 +2,13 @@ RSpec.describe GeoArea, type: :model do
|
||||||
describe '#area' do
|
describe '#area' do
|
||||||
let(:geo_area) { build(:geo_area, :polygon) }
|
let(:geo_area) { build(:geo_area, :polygon) }
|
||||||
|
|
||||||
it { expect(geo_area.area).to eq(219.0) }
|
it { expect(geo_area.area).to eq(103.6) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#area (hourglass polygon)' do
|
describe '#area (hourglass polygon)' do
|
||||||
let(:geo_area) { build(:geo_area, :hourglass_polygon) }
|
let(:geo_area) { build(:geo_area, :hourglass_polygon) }
|
||||||
|
|
||||||
# This test fails in my local environement end the problem exists in production.
|
it { expect(geo_area.area).to eq(32.4) }
|
||||||
# Must be some mismatch between CI/production. I still want this fix in production.
|
|
||||||
it.pending { expect(geo_area.area).to be_nil }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#length' do
|
describe '#length' do
|
||||||
|
|
Loading…
Reference in a new issue