Add compatibility cadsatre layer with old API GEO

This commit is contained in:
Paul Chavard 2021-05-06 18:50:06 +02:00
parent e74dcb0056
commit 3b85ade440
11 changed files with 206 additions and 155 deletions

View file

@ -4,29 +4,26 @@ class Champs::CarteController < ApplicationController
def index def index
@selector = ".carte-#{params[:champ_id]}" @selector = ".carte-#{params[:champ_id]}"
@champ = policy_scope(Champ).find(params[:champ_id]) @champ = policy_scope(Champ).find(params[:champ_id])
@update_cadastres = params[:cadastres] @focus = params[:focus].present?
if @champ.cadastres? && @update_cadastres
@champ.geo_areas.cadastres.destroy_all
@champ.geo_areas += GeoArea.from_feature_collection(cadastres_features_collection(@champ.to_feature_collection))
@champ.save!
end
rescue APICarto::API::ResourceNotFound
flash.alert = 'Les données cartographiques sont temporairement indisponibles. Réessayez dans un instant.'
response.status = 503
end end
def create def create
champ = policy_scope(Champ).find(params[:champ_id]) champ = policy_scope(Champ).find(params[:champ_id])
geo_area = champ.geo_areas.selections_utilisateur.new geo_area = if params_source == GeoArea.sources.fetch(:cadastre)
save_feature!(geo_area, params_feature) champ.geo_areas.find_by("properties->>'id' = :id", id: params_feature[:properties][:id])
end
if geo_area.nil?
geo_area = champ.geo_areas.build(source: params_source, properties: {})
save_feature!(geo_area, params_feature)
end
render json: { feature: geo_area.to_feature }, status: :created render json: { feature: geo_area.to_feature }, status: :created
end end
def update def update
champ = policy_scope(Champ).find(params[:champ_id]) champ = policy_scope(Champ).find(params[:champ_id])
geo_area = champ.geo_areas.selections_utilisateur.find(params[:id]) geo_area = champ.geo_areas.find(params[:id])
save_feature!(geo_area, params_feature) save_feature!(geo_area, params_feature)
head :no_content head :no_content
@ -34,66 +31,42 @@ class Champs::CarteController < ApplicationController
def destroy def destroy
champ = policy_scope(Champ).find(params[:champ_id]) champ = policy_scope(Champ).find(params[:champ_id])
champ.geo_areas.selections_utilisateur.find(params[:id]).destroy! champ.geo_areas.find(params[:id]).destroy!
head :no_content head :no_content
end end
def import
champ = policy_scope(Champ).find(params[:champ_id])
params_features.each do |feature|
geo_area = champ.geo_areas.selections_utilisateur.new
save_feature!(geo_area, feature)
end
render json: champ.to_feature_collection, status: :created
end
private private
def params_feature def params_source
params[:feature] params[:source]
end end
def params_features def params_feature
params[:features] params.require(:feature).permit(properties: [
:filename,
:description,
:arpente,
:commune,
:contenance,
:created,
:id,
:numero,
:prefixe,
:section,
:updated
]).tap do |feature|
feature[:geometry] = params[:feature][:geometry]
end
end end
def save_feature!(geo_area, feature) def save_feature!(geo_area, feature)
if feature[:geometry] if feature[:geometry]
geo_area.geometry = feature[:geometry] geo_area.geometry = feature[:geometry]
end end
if feature[:properties] && feature[:properties][:description] if feature[:properties]
geo_area.description = feature[:properties][:description] geo_area.properties.merge!(feature[:properties])
end end
geo_area.save! geo_area.save!
end end
def cadastres_features_collection(feature_collection)
coordinates = feature_collection[:features].filter do |feature|
feature[:properties][:source] == GeoArea.sources.fetch(:selection_utilisateur) && feature[:geometry] && feature[:geometry]['type'] == 'Polygon'
end.map do |feature|
feature[:geometry]['coordinates'][0].map { |(lng, lat)| { 'lng' => lng, 'lat' => lat } }
end
if coordinates.present?
cadastres = APICartoService.generate_cadastre(coordinates)
{
type: 'FeatureCollection',
features: cadastres.map do |cadastre|
{
type: 'Feature',
geometry: cadastre.delete(:geometry),
properties: cadastre.merge(source: GeoArea.sources.fetch(:cadastre))
}
end
}
else
{
type: 'FeatureCollection',
features: []
}
end
end
end end

View file

@ -1467,18 +1467,21 @@ type PageInfo {
} }
type ParcelleCadastrale implements GeoArea { type ParcelleCadastrale implements GeoArea {
codeArr: String! codeArr: String! @deprecated(reason: "Utilisez le champ `prefixe` à la place.")
codeCom: String! codeCom: String! @deprecated(reason: "Utilisez le champ `commune` à la place.")
codeDep: String! codeDep: String! @deprecated(reason: "Utilisez le champ `commune` à la place.")
feuille: Int! commune: String!
feuille: Int! @deprecated(reason: "Linformation nest plus disponible.")
geometry: GeoJSON! geometry: GeoJSON!
id: ID! id: ID!
nomCom: String! nomCom: String! @deprecated(reason: "Utilisez le champ `commune` à la place.")
numero: String! numero: String!
prefixe: String!
section: String! section: String!
source: GeoAreaSource! source: GeoAreaSource!
surfaceIntersection: Float! surface: String!
surfaceParcelle: Float! surfaceIntersection: Float! @deprecated(reason: "Linformation nest plus disponible.")
surfaceParcelle: Float! @deprecated(reason: "Utilisez le champ `surface` à la place.")
} }
type PersonneMorale implements Demandeur { type PersonneMorale implements Demandeur {
@ -1587,6 +1590,7 @@ type Revision {
} }
type SelectionUtilisateur implements GeoArea { type SelectionUtilisateur implements GeoArea {
description: String!
geometry: GeoJSON! geometry: GeoJSON!
id: ID! id: ID!
source: GeoAreaSource! source: GeoAreaSource!

View file

@ -2,14 +2,18 @@ module Types::GeoAreas
class ParcelleCadastraleType < Types::BaseObject class ParcelleCadastraleType < Types::BaseObject
implements Types::GeoAreaType implements Types::GeoAreaType
field :surface_intersection, Float, null: false
field :surface_parcelle, Float, null: false
field :numero, String, null: false field :numero, String, null: false
field :feuille, Int, null: false
field :section, String, null: false field :section, String, null: false
field :code_dep, String, null: false field :surface, String, null: false
field :nom_com, String, null: false field :prefixe, String, null: false
field :code_com, String, null: false field :commune, String, null: false
field :code_arr, String, null: false
field :code_dep, String, null: false, deprecation_reason: 'Utilisez le champ `commune` à la place.'
field :nom_com, String, null: false, deprecation_reason: 'Utilisez le champ `commune` à la place.'
field :code_com, String, null: false, deprecation_reason: 'Utilisez le champ `commune` à la place.'
field :code_arr, String, null: false, deprecation_reason: 'Utilisez le champ `prefixe` à la place.'
field :feuille, Int, null: false, deprecation_reason: 'Linformation nest plus disponible.'
field :surface_intersection, Float, null: false, deprecation_reason: 'Linformation nest plus disponible.'
field :surface_parcelle, Float, null: false, deprecation_reason: 'Utilisez le champ `surface` à la place.'
end end
end end

View file

@ -1,5 +1,7 @@
module Types::GeoAreas module Types::GeoAreas
class SelectionUtilisateurType < Types::BaseObject class SelectionUtilisateurType < Types::BaseObject
implements Types::GeoAreaType implements Types::GeoAreaType
field :description, String, null: false
end end
end end

View file

@ -36,7 +36,7 @@ module ChampHelper
case geo_area.source case geo_area.source
when GeoArea.sources.fetch(:cadastre) when GeoArea.sources.fetch(:cadastre)
capture do capture do
concat "Parcelle n° #{geo_area.numero} - Feuille #{geo_area.code_arr} #{geo_area.section} #{geo_area.feuille} - #{geo_area.surface_parcelle.round} m" concat "Parcelle n° #{geo_area.numero} - Feuille #{geo_area.prefixe} #{geo_area.section} - #{geo_area.surface.round} m"
concat tag.sup("2") concat tag.sup("2")
end end
when GeoArea.sources.fetch(:selection_utilisateur) when GeoArea.sources.fetch(:selection_utilisateur)

View file

@ -14,25 +14,33 @@
class GeoArea < ApplicationRecord class GeoArea < ApplicationRecord
belongs_to :champ, optional: false belongs_to :champ, optional: false
store :properties, accessors: [ # FIXME: once geo_areas are migrated to not use YAML serialization we can enable store_accessor
:description, # store_accessor :properties, :description, :numero, :section
:surface_intersection,
:surface_parcelle, def properties
:numero, value = read_attribute(:properties)
:feuille, if value.is_a? String
:section, ActiveRecord::Coders::YAMLColumn.new(:properties).load(value)
:code_dep, else
:nom_com, value
:code_com, end
:code_arr, end
:code,
:nom, def description
:commune, properties['description']
:culture, end
:code_culture,
:surface, def numero
:bio properties['numero']
] end
def section
properties['section']
end
def filename
properties['filename']
end
enum source: { enum source: {
cadastre: 'cadastre', cadastre: 'cadastre',
@ -48,10 +56,12 @@ class GeoArea < ApplicationRecord
{ {
type: 'Feature', type: 'Feature',
geometry: safe_geometry, geometry: safe_geometry,
properties: properties.symbolize_keys.merge( properties: cadastre_properties.merge(
source: source, source: source,
area: area, area: area,
length: length, length: length,
description: description,
filename: filename,
id: id, id: id,
champ_id: champ.stable_id, champ_id: champ.stable_id,
dossier_id: champ.dossier_id dossier_id: champ.dossier_id
@ -69,16 +79,6 @@ class GeoArea < ApplicationRecord
nil nil
end end
def self.from_feature_collection(feature_collection)
feature_collection[:features].map do |feature|
GeoArea.new(
source: feature[:properties].delete(:source),
properties: feature[:properties],
geometry: feature[:geometry]
)
end
end
def area def area
if polygon? if polygon?
GeojsonService.area(geometry.deep_symbolize_keys).round(1) GeojsonService.area(geometry.deep_symbolize_keys).round(1)
@ -108,4 +108,107 @@ class GeoArea < ApplicationRecord
def point? def point?
geometry['type'] == 'Point' geometry['type'] == 'Point'
end end
def legacy_cadastre?
cadastre? && properties['surface_intersection'].present?
end
def cadastre?
source == GeoArea.sources.fetch(:cadastre)
end
def cadastre_properties
if cadastre?
{
cid: cid,
numero: numero,
section: section,
prefixe: prefixe,
commune: commune,
surface: surface
}
else
{}
end
end
def code_dep
if legacy_cadastre?
properties['code_dep']
else
properties['commune'][0..1]
end
end
def code_com
if legacy_cadastre?
properties['code_com']
else
properties['commune'][2...commune.size]
end
end
def nom_com
if legacy_cadastre?
properties['nom_com']
else
''
end
end
def surface_intersection
if legacy_cadastre?
properties['surface_intersection']
else
''
end
end
def feuille
if legacy_cadastre?
properties['feuille']
else
1
end
end
def code_arr
prefixe
end
def surface_parcelle
surface
end
def surface
if legacy_cadastre?
properties['surface_parcelle']
else
properties['contenance']
end
end
def prefixe
if legacy_cadastre?
properties['code_arr']
else
properties['prefixe']
end
end
def commune
if legacy_cadastre?
"#{properties['code_dep']}#{properties['code_com']}"
else
properties['commune']
end
end
def cid
if legacy_cadastre?
"#{code_dep}#{code_com}#{code_arr}#{section}#{numero}"
else
properties['id']
end
end
end end

View file

@ -4,6 +4,6 @@
partial: 'shared/champs/carte/geo_areas', partial: 'shared/champs/carte/geo_areas',
locals: { champ: @champ, editing: true }) %> locals: { champ: @champ, editing: true }) %>
<% if @update_cadastres %> <% if @focus %>
<%= fire_event('cadastres:update', { featureCollection: @champ.to_feature_collection }.to_json) %> <%= fire_event('map:feature:focus', { bbox: @champ.bounding_box }.to_json) %>
<% end %> <% end %>

View file

@ -7,7 +7,7 @@
- if editing - if editing
= link_to '#', data: { geo_area: geo_area.id } do = link_to '#', data: { geo_area: geo_area.id } do
= geo_area_label(geo_area) = geo_area_label(geo_area)
= text_field_tag :description, geo_area.description, data: { geo_area: geo_area.id }, placeholder: 'Description de la sélection' = text_field_tag :description, geo_area.description, data: { geo_area: geo_area.id }, placeholder: 'Description de la sélection', class: 'no-margin'
- else - else
= link_to '#', data: { geo_area: geo_area.id } do = link_to '#', data: { geo_area: geo_area.id } do
= geo_area_label(geo_area) = geo_area_label(geo_area)

View file

@ -25,7 +25,8 @@ describe Champs::CarteController, type: :controller do
let(:params) do let(:params) do
{ {
champ_id: champ.id, champ_id: champ.id,
feature: feature feature: feature,
source: GeoArea.sources.fetch(:selection_utilisateur)
} }
end end
@ -98,67 +99,37 @@ describe Champs::CarteController, type: :controller do
it { expect(response.status).to eq 204 } it { expect(response.status).to eq 204 }
end end
describe 'POST #import' do describe 'GET #index' do
render_views render_views
let(:params) do let(:params) do
{ { champ_id: champ.id }
champ_id: champ.id,
features: [feature]
}
end end
before do
post :import, params: params
end
it {
expect(response.status).to eq 201
expect(response.body).to include("bbox")
}
end
describe 'GET #index' do
render_views
before do before do
request.accept = "application/javascript" request.accept = "application/javascript"
request.content_type = "application/javascript" request.content_type = "application/javascript"
get :index, params: params
end end
context 'with cadastres update' do context "update list" do
let(:params) do
{
champ_id: champ.id,
cadastres: 'update'
}
end
before do
get :index, params: params
end
it { it {
expect(response.body).not_to include("DS.fire('map:feature:focus'")
expect(response.status).to eq 200 expect(response.status).to eq 200
expect(response.body).to include("DS.fire('cadastres:update'")
} }
end end
context 'without cadastres update' do context "update list and focus" do
let(:params) do let(:params) do
{ {
champ_id: champ.id champ_id: champ.id,
focus: true
} }
end end
before do
get :index, params: params
end
it { it {
expect(response.body).to include("DS.fire('map:feature:focus'")
expect(response.status).to eq 200 expect(response.status).to eq 200
expect(response.body).not_to include("DS.fire('cadastres:update'")
} }
end end
end end

View file

@ -1,17 +1,11 @@
FactoryBot.define do FactoryBot.define do
factory :geo_area do factory :geo_area do
association :champ association :champ
properties { {} }
trait :cadastre do trait :cadastre do
source { GeoArea.sources.fetch(:cadastre) } source { GeoArea.sources.fetch(:cadastre) }
numero { '42' } properties { { numero: '42', section: 'A11', commune: '75127' } }
feuille { 'A11' }
end
trait :quartier_prioritaire do
source { GeoArea.sources.fetch(:quartier_prioritaire) }
nom { 'XYZ' }
commune { 'Paris' }
end end
trait :selection_utilisateur do trait :selection_utilisateur do

View file

@ -85,7 +85,7 @@ describe ChampSerializer do
source: GeoArea.sources.fetch(:cadastre), source: GeoArea.sources.fetch(:cadastre),
geometry: geo_json, geometry: geo_json,
numero: '42', numero: '42',
feuille: 'A11' section: 'A11'
) )
expect(subject[:geo_areas].first.key?(:nom)).to be_falsey expect(subject[:geo_areas].first.key?(:nom)).to be_falsey
} }