Merge pull request #5616 from coslajohn/maps
Render Geozone Maps Collections and Multipolygons
This commit is contained in:
@@ -14,3 +14,4 @@
|
|||||||
//= link print.css
|
//= link print.css
|
||||||
//= link pdf_fonts.css
|
//= link pdf_fonts.css
|
||||||
//= link_tree ../../../vendor/assets/images
|
//= link_tree ../../../vendor/assets/images
|
||||||
|
//= link_tree ../../../node_modules/leaflet/dist/images
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
App.Map.maps = [];
|
App.Map.maps = [];
|
||||||
},
|
},
|
||||||
initializeMap: function(element) {
|
initializeMap: function(element) {
|
||||||
var createMarker, editable, investmentsMarkers, map, marker, markerClustering,
|
var createMarker, editable, geozoneLayers, investmentsMarkers, map, marker, markerClustering,
|
||||||
markerData, markerIcon, markers, moveOrPlaceMarker, removeMarker, removeMarkerSelector;
|
markerData, markerIcon, markers, moveOrPlaceMarker, removeMarker, removeMarkerSelector;
|
||||||
App.Map.cleanInvestmentCoordinates(element);
|
App.Map.cleanInvestmentCoordinates(element);
|
||||||
removeMarkerSelector = $(element).data("marker-remove-selector");
|
removeMarkerSelector = $(element).data("marker-remove-selector");
|
||||||
@@ -84,7 +84,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
App.Map.addInvestmentsMarkers(investmentsMarkers, createMarker);
|
App.Map.addInvestmentsMarkers(investmentsMarkers, createMarker);
|
||||||
App.Map.addGeozones(map);
|
geozoneLayers = App.Map.geozoneLayers(map);
|
||||||
|
App.Map.addGeozones(map, geozoneLayers);
|
||||||
|
App.Map.addLayerControl(map, geozoneLayers);
|
||||||
|
|
||||||
map.addLayer(markers);
|
map.addLayer(markers);
|
||||||
},
|
},
|
||||||
leafletMap: function(element) {
|
leafletMap: function(element) {
|
||||||
@@ -210,27 +213,52 @@
|
|||||||
map.attributionControl.setPrefix(App.Map.attributionPrefix());
|
map.attributionControl.setPrefix(App.Map.attributionPrefix());
|
||||||
L.tileLayer(mapTilesProvider, { attribution: mapAttribution }).addTo(map);
|
L.tileLayer(mapTilesProvider, { attribution: mapAttribution }).addTo(map);
|
||||||
},
|
},
|
||||||
addGeozones: function(map) {
|
addGeozones: function(map, geozoneLayers) {
|
||||||
|
$.each(geozoneLayers, function(_, geozoneLayer) {
|
||||||
|
App.Map.addGeozone(map, geozoneLayer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addLayerControl: function(map, geozoneLayers) {
|
||||||
|
if (Object.keys(geozoneLayers).length > 1) {
|
||||||
|
L.control.layers(null, geozoneLayers).addTo(map);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
geozoneLayers: function(map) {
|
||||||
var geozones = $(map._container).data("geozones");
|
var geozones = $(map._container).data("geozones");
|
||||||
|
var layers = {};
|
||||||
|
|
||||||
if (geozones) {
|
if (geozones) {
|
||||||
geozones.forEach(function(geozone) {
|
geozones.forEach(function(geozone) {
|
||||||
App.Map.addGeozone(geozone, map);
|
if (geozone.outline_points) {
|
||||||
|
layers[geozone.name] = App.Map.geozoneLayer(geozone);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return layers;
|
||||||
},
|
},
|
||||||
addGeozone: function(geozone, map) {
|
geozoneLayer: function(geozone) {
|
||||||
var polygon = L.polygon(geozone.outline_points, {
|
var geojsonData = JSON.parse(geozone.outline_points);
|
||||||
color: geozone.color,
|
|
||||||
|
return L.geoJSON(geojsonData, {
|
||||||
|
style: function(feature) {
|
||||||
|
return {
|
||||||
|
color: feature.properties.color || geozone.color,
|
||||||
fillOpacity: 0.3,
|
fillOpacity: 0.3,
|
||||||
className: "map-polygon"
|
className: "map-polygon"
|
||||||
});
|
};
|
||||||
|
},
|
||||||
|
onEachFeature: function(feature, layer) {
|
||||||
|
var headings = feature.properties.headings || geozone.headings;
|
||||||
|
|
||||||
if (geozone.headings !== undefined) {
|
if (headings) {
|
||||||
polygon.bindPopup(geozone.headings.join("<br>"));
|
layer.bindPopup(headings.join("<br>"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
polygon.addTo(map);
|
});
|
||||||
|
},
|
||||||
|
addGeozone: function(map, geozoneLayer) {
|
||||||
|
geozoneLayer.addTo(map);
|
||||||
},
|
},
|
||||||
getPopupContent: function(data) {
|
getPopupContent: function(data) {
|
||||||
return "<a href='" + data.link + "'>" + data.title + "</a>";
|
return "<a href='" + data.link + "'>" + data.title + "</a>";
|
||||||
|
|||||||
@@ -34,5 +34,5 @@
|
|||||||
<h3><%= t("admin.geozones.index.geojson_map") %></h3>
|
<h3><%= t("admin.geozones.index.geojson_map") %></h3>
|
||||||
<p class="help-text"><%= t("admin.geozones.index.geojson_map_help") %></p>
|
<p class="help-text"><%= t("admin.geozones.index.geojson_map_help") %></p>
|
||||||
|
|
||||||
<%= render Shared::MapLocationComponent.new(nil, geozones_data: geozones_data) %>
|
<%= render_map(nil, geozones_data: geozones_data) %>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
class Admin::Geozones::IndexComponent < ApplicationComponent
|
class Admin::Geozones::IndexComponent < ApplicationComponent
|
||||||
include Header
|
include Header
|
||||||
attr_reader :geozones
|
attr_reader :geozones
|
||||||
|
use_helpers :render_map
|
||||||
|
|
||||||
def initialize(geozones)
|
def initialize(geozones)
|
||||||
@geozones = geozones
|
@geozones = geozones
|
||||||
@@ -25,7 +26,8 @@ class Admin::Geozones::IndexComponent < ApplicationComponent
|
|||||||
{
|
{
|
||||||
outline_points: geozone.outline_points,
|
outline_points: geozone.outline_points,
|
||||||
color: geozone.color,
|
color: geozone.color,
|
||||||
headings: [link_to(geozone.name, edit_admin_geozone_path(geozone))]
|
headings: [link_to(geozone.name, edit_admin_geozone_path(geozone))],
|
||||||
|
name: geozone.name
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class Budgets::Investments::MapComponent < ApplicationComponent
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
outline_points: heading.geozone.outline_points,
|
outline_points: heading.geozone.outline_points,
|
||||||
color: heading.geozone.color
|
color: heading.geozone.color,
|
||||||
|
name: heading.name
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,10 +27,15 @@ class Budgets::MapComponent < ApplicationComponent
|
|||||||
{
|
{
|
||||||
outline_points: geozone.outline_points,
|
outline_points: geozone.outline_points,
|
||||||
color: geozone.color,
|
color: geozone.color,
|
||||||
headings: budget.headings.where(geozone: geozone).map do |heading|
|
headings: geozone_headings(geozone).map do |heading|
|
||||||
link_to heading.name, budget_investments_path(budget, heading_id: heading.id)
|
link_to heading.name, budget_investments_path(budget, heading_id: heading.id)
|
||||||
end
|
end,
|
||||||
|
name: geozone_headings(geozone).map(&:name).join(", ")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def geozone_headings(geozone)
|
||||||
|
budget.headings.where(geozone: geozone)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ class GeojsonFormatValidator < ActiveModel::EachValidator
|
|||||||
if value.present?
|
if value.present?
|
||||||
geojson = parse_json(value)
|
geojson = parse_json(value)
|
||||||
|
|
||||||
unless geojson?(geojson)
|
unless valid_geojson?(geojson)
|
||||||
record.errors.add(attribute, :invalid)
|
record.errors.add(attribute, :invalid)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
unless valid_coordinates?(geojson)
|
||||||
|
record.errors.add(attribute, :invalid_coordinates)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -12,12 +17,111 @@ class GeojsonFormatValidator < ActiveModel::EachValidator
|
|||||||
private
|
private
|
||||||
|
|
||||||
def parse_json(geojson_data)
|
def parse_json(geojson_data)
|
||||||
JSON.parse(geojson_data) rescue nil
|
JSON.parse(geojson_data)
|
||||||
|
rescue JSON::ParserError
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def geojson?(geojson)
|
def valid_geojson?(geojson)
|
||||||
return false unless geojson.is_a?(Hash)
|
return false unless geojson.is_a?(Hash)
|
||||||
|
|
||||||
geojson.dig("geometry", "coordinates").is_a?(Array)
|
if geojson["type"] == "FeatureCollection"
|
||||||
|
valid_feature_collection?(geojson)
|
||||||
|
elsif geojson["type"] == "Feature"
|
||||||
|
valid_feature?(geojson)
|
||||||
|
else
|
||||||
|
valid_geometry?(geojson)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_feature_collection?(geojson)
|
||||||
|
return false unless geojson["features"].is_a?(Array)
|
||||||
|
|
||||||
|
geojson["features"].all? { |feature| valid_feature?(feature) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_feature?(feature)
|
||||||
|
feature["type"] == "Feature" && valid_geometry?(feature["geometry"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_geometry?(geometry)
|
||||||
|
geometry.is_a?(Hash) && valid_geometry_types.include?(geometry["type"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_geometry_types
|
||||||
|
[
|
||||||
|
"Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon",
|
||||||
|
"GeometryCollection"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_coordinates?(geojson)
|
||||||
|
if geojson["type"] == "FeatureCollection"
|
||||||
|
geojson["features"].all? { |feature| valid_coordinates?(feature) }
|
||||||
|
elsif geojson["type"] == "Feature"
|
||||||
|
valid_geometry_coordinates?(geojson["geometry"])
|
||||||
|
else
|
||||||
|
valid_geometry_coordinates?(geojson)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_geometry_coordinates?(geometry)
|
||||||
|
if geometry["type"] == "GeometryCollection"
|
||||||
|
geometries = geometry["geometries"]
|
||||||
|
|
||||||
|
return geometries.is_a?(Array) && geometries.all? { |geom| valid_geometry_coordinates?(geom) }
|
||||||
|
end
|
||||||
|
|
||||||
|
coordinates = geometry["coordinates"]
|
||||||
|
|
||||||
|
return false unless coordinates.is_a?(Array)
|
||||||
|
|
||||||
|
case geometry["type"]
|
||||||
|
when "Point"
|
||||||
|
valid_wgs84_coordinates?(coordinates)
|
||||||
|
when "LineString"
|
||||||
|
valid_linestring_coordinates?(coordinates)
|
||||||
|
when "MultiPoint"
|
||||||
|
valid_coordinates_array?(coordinates)
|
||||||
|
when "MultiLineString"
|
||||||
|
coordinates.all? do |linestring_coordinates|
|
||||||
|
valid_linestring_coordinates?(linestring_coordinates)
|
||||||
|
end
|
||||||
|
when "Polygon"
|
||||||
|
valid_polygon_coordinates?(coordinates)
|
||||||
|
when "MultiPolygon"
|
||||||
|
coordinates.all? do |polygon_coordinates|
|
||||||
|
valid_polygon_coordinates?(polygon_coordinates)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_wgs84_coordinates?(coordinates)
|
||||||
|
return false unless coordinates.is_a?(Array) && coordinates.size == 2
|
||||||
|
|
||||||
|
longitude, latitude = coordinates
|
||||||
|
(-180.0..180.0).include?(longitude) && (-90.0..90.0).include?(latitude)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_coordinates_array?(coordinates_array)
|
||||||
|
coordinates_array.is_a?(Array) &&
|
||||||
|
coordinates_array.all? { |coordinates| valid_wgs84_coordinates?(coordinates) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_linestring_coordinates?(coordinates)
|
||||||
|
valid_coordinates_array?(coordinates) && coordinates.many?
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_polygon_coordinates?(polygon_coordinates)
|
||||||
|
polygon_coordinates.is_a?(Array) &&
|
||||||
|
polygon_coordinates.all? { |ring_coordinates| valid_ring_coordinates?(ring_coordinates) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_ring_coordinates?(ring_coordinates)
|
||||||
|
valid_coordinates_array?(ring_coordinates) &&
|
||||||
|
ring_coordinates.size >= 4 &&
|
||||||
|
ring_coordinates.first == ring_coordinates.last
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
class Geozone < ApplicationRecord
|
class Geozone < ApplicationRecord
|
||||||
include Graphqlable
|
include Graphqlable
|
||||||
|
|
||||||
|
attribute :color, default: "#0000ff"
|
||||||
|
|
||||||
has_many :proposals
|
has_many :proposals
|
||||||
has_many :debates
|
has_many :debates
|
||||||
has_many :users
|
has_many :users
|
||||||
@@ -21,26 +23,49 @@ class Geozone < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def outline_points
|
def outline_points
|
||||||
normalized_coordinates.map { |longlat| [longlat.last, longlat.first] }
|
normalized_geojson&.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def normalized_coordinates
|
def normalized_geojson
|
||||||
if geojson.present?
|
if geojson.present?
|
||||||
if geojson.match(/"coordinates"\s*:\s*\[\s*\[\s*\[\s*\[/)
|
parsed_geojson = JSON.parse(geojson)
|
||||||
coordinates.reduce([], :concat).reduce([], :concat)
|
|
||||||
elsif geojson.match(/"coordinates"\s*:\s*\[\s*\[\s*\[/)
|
if parsed_geojson["type"] == "FeatureCollection"
|
||||||
coordinates.reduce([], :concat)
|
parsed_geojson["features"].each do |feature|
|
||||||
else
|
feature["properties"] ||= {}
|
||||||
coordinates
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
parsed_geojson
|
||||||
|
elsif parsed_geojson["type"] == "Feature"
|
||||||
|
parsed_geojson["properties"] ||= {}
|
||||||
|
|
||||||
|
wrap_in_feature_collection(parsed_geojson)
|
||||||
|
elsif parsed_geojson["geometry"]
|
||||||
|
parsed_geojson["properties"] ||= {}
|
||||||
|
|
||||||
|
wrap_in_feature_collection(wrap_in_feature(parsed_geojson["geometry"]))
|
||||||
|
elsif parsed_geojson["type"] && parsed_geojson["coordinates"]
|
||||||
|
wrap_in_feature_collection(wrap_in_feature(parsed_geojson))
|
||||||
else
|
else
|
||||||
[]
|
raise ArgumentError, "Invalid GeoJSON fragment"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def coordinates
|
def wrap_in_feature(geometry)
|
||||||
JSON.parse(geojson)["geometry"]["coordinates"]
|
{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: geometry,
|
||||||
|
properties: {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_in_feature_collection(feature)
|
||||||
|
{
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: [feature]
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ module Consul
|
|||||||
config.assets.paths << Rails.root.join("app", "assets", "fonts")
|
config.assets.paths << Rails.root.join("app", "assets", "fonts")
|
||||||
config.assets.paths << Rails.root.join("vendor", "assets", "fonts")
|
config.assets.paths << Rails.root.join("vendor", "assets", "fonts")
|
||||||
config.assets.paths << Rails.root.join("node_modules", "jquery-ui", "themes", "base")
|
config.assets.paths << Rails.root.join("node_modules", "jquery-ui", "themes", "base")
|
||||||
|
config.assets.paths << Rails.root.join("node_modules", "leaflet", "dist")
|
||||||
config.assets.paths << Rails.root.join("node_modules")
|
config.assets.paths << Rails.root.join("node_modules")
|
||||||
|
|
||||||
config.active_job.queue_adapter = :delayed_job
|
config.active_job.queue_adapter = :delayed_job
|
||||||
|
|||||||
@@ -551,7 +551,8 @@ en:
|
|||||||
geozone:
|
geozone:
|
||||||
attributes:
|
attributes:
|
||||||
geojson:
|
geojson:
|
||||||
invalid: "The GeoJSON provided does not follow the correct format. It must follow the \"Polygon\" or \"MultiPolygon\" type format."
|
invalid: "The GeoJSON provided does not follow the correct format. It must follow the RFC 7946 standard format"
|
||||||
|
invalid_coordinates: "The GeoJSON provided contains invalid coordinates; the coordinates must be in the required \"Longitude, Latitude\" format and follow the RFC 7946 standard format"
|
||||||
image:
|
image:
|
||||||
attributes:
|
attributes:
|
||||||
attachment:
|
attachment:
|
||||||
|
|||||||
@@ -551,7 +551,8 @@ es:
|
|||||||
geozone:
|
geozone:
|
||||||
attributes:
|
attributes:
|
||||||
geojson:
|
geojson:
|
||||||
invalid: "Los datos GeoJSON proporcionados no tienen el formato correcto. Deben tener un tipo del formato \"Polygon\" o \"MultiPolygon\"."
|
invalid: "Los datos GeoJSON proporcionados no tienen el formato correcto. Deben seguir el formato estándar RFC 7946"
|
||||||
|
invalid_coordinates: "Los datos GeoJSON proporcionados contienen coordenadas inválidas; las coordenadas deben utilizar el formato \"Longitud, Latitud\" y seguir el formato estándar RFC 7946"
|
||||||
image:
|
image:
|
||||||
attributes:
|
attributes:
|
||||||
attachment:
|
attachment:
|
||||||
|
|||||||
@@ -20,7 +20,15 @@ FactoryBot.define do
|
|||||||
|
|
||||||
trait :with_geojson do
|
trait :with_geojson do
|
||||||
geojson do
|
geojson do
|
||||||
'{ "geometry": { "type": "Polygon", "coordinates": [[0.117,51.513],[0.118,51.512],[0.119,51.514]] } }'
|
<<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[0.117, 51.513], [0.118, 51.512], [0.119, 51.514], [0.117, 51.513]]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
608
spec/models/geojson_format_validator_spec.rb
Normal file
608
spec/models/geojson_format_validator_spec.rb
Normal file
@@ -0,0 +1,608 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe GeojsonFormatValidator do
|
||||||
|
before do
|
||||||
|
dummy_model = Class.new do
|
||||||
|
include ActiveModel::Model
|
||||||
|
attr_accessor :geojson
|
||||||
|
validates :geojson, geojson_format: true
|
||||||
|
end
|
||||||
|
|
||||||
|
stub_const("DummyModel", dummy_model)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:record) { DummyModel.new }
|
||||||
|
|
||||||
|
it "is not valid with an empty hash" do
|
||||||
|
record.geojson = "{}"
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with arbitrary keys" do
|
||||||
|
record.geojson = '{ "invalid": "yes" }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid without a type" do
|
||||||
|
record.geojson = '{ "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid without a type but a geometry" do
|
||||||
|
record.geojson = '{ "geometry": { "type": "Point", "coordinates": [1.23, 4.56] } }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Point geometry" do
|
||||||
|
it "is not valid without coordinates" do
|
||||||
|
record.geojson = '{ "type": "Point" }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with only one the longitude" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": 1.23 }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with non-numerical coordinates" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": ["1.23", "4.56"] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with 3-dimensional coordinates" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [1.23, 4.56, 7.89] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with multiple coordinates" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [[1.23, 4.56], [7.89, 10.11]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a longitude above 180" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [180.01, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a longitude below -180" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [-180.01, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a latitude above 90" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [1.23, 90.01] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a latitude below -90" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [1.23, -90.01] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with coordinates in the valid range" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with coordinates at the positive end of the range" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [180.0, 90.0] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with coordinates at the negative end of the range" do
|
||||||
|
record.geojson = '{ "type": "Point", "coordinates": [-180.0, -90.0] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "LineString or MultiPoint geometry" do
|
||||||
|
it "is not valid with a one-dimensional array of coordinates" do
|
||||||
|
record.geojson = '{ "type": "LineString", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
|
||||||
|
record.geojson = '{ "type": "MultiPoint", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid when some coordinates are invalid" do
|
||||||
|
record.geojson = '{ "type": "LineString", "coordinates": [[1.23, 4.56], [180.01, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
|
||||||
|
record.geojson = '{ "type": "MultiPoint", "coordinates": [[1.23, 4.56], [180.01, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid when all the coordinates are valid" do
|
||||||
|
record.geojson = '{ "type": "LineString", "coordinates": [[1.23, 4.56], [7.89, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
|
||||||
|
record.geojson = '{ "type": "MultiPoint", "coordinates": [[1.23, 4.56], [7.89, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "LineString geometry" do
|
||||||
|
it "is not valid with only one point" do
|
||||||
|
record.geojson = '{ "type": "LineString", "coordinates": [[1.23, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "MultiPoint geometry" do
|
||||||
|
it "is valid with only one point" do
|
||||||
|
record.geojson = '{ "type": "MultiPoint", "coordinates": [[1.23, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Polygon or MultiLineString geometry" do
|
||||||
|
it "is not valid with a one-dimensional array of coordinates" do
|
||||||
|
record.geojson = '{ "type": "MultiLineString", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
|
||||||
|
record.geojson = '{ "type": "Polygon", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a two-dimensional array of coordinates" do
|
||||||
|
record.geojson = '{ "type": "MultiLineString", "coordinates": [[1.23, 4.56], [7.89, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
|
||||||
|
record.geojson = '{ "type": "Polygon", "coordinates": [[1.23, 4.56], [7.89, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "MultiLineString geometry" do
|
||||||
|
it "is valid with just one line" do
|
||||||
|
record.geojson = '{ "type": "MultiLineString", "coordinates": [[[1.23, 4.56], [7.89, 4.56]]] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with multiple valid lines" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiLineString",
|
||||||
|
"coordinates": [
|
||||||
|
[[1.23, 4.56], [7.89, 4.56]],
|
||||||
|
[[10.11, 12.13], [14.15, 16.17]]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid if some lines are invalid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiLineString",
|
||||||
|
"coordinates": [
|
||||||
|
[[1.23, 4.56], [7.89, 4.56]],
|
||||||
|
[[10.11, 12.13]]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Polygon geometry" do
|
||||||
|
it "is not valid with a ring having less than four elements" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a ring which with different starting and end points" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[16.17, 18.19]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with one valid ring" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with multiple valid rings" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [
|
||||||
|
[
|
||||||
|
[100.0, 0.0],
|
||||||
|
[101.0, 0.0],
|
||||||
|
[101.0, 1.0],
|
||||||
|
[100.0, 1.0],
|
||||||
|
[100.0, 0.0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[100.8, 0.8],
|
||||||
|
[100.8, 0.2],
|
||||||
|
[100.2, 0.2],
|
||||||
|
[100.2, 0.8],
|
||||||
|
[100.8, 0.8]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with multiple rings if some rings are invalid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [
|
||||||
|
[
|
||||||
|
[100.0, 0.0],
|
||||||
|
[101.0, 0.0],
|
||||||
|
[101.0, 1.0],
|
||||||
|
[100.0, 1.0],
|
||||||
|
[100.0, 0.0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[100.8, 0.8],
|
||||||
|
[100.8, 0.2],
|
||||||
|
[100.2, 0.2]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "MultiPolygon geometry" do
|
||||||
|
it "is not valid with a one-dimensional array of coordinates" do
|
||||||
|
record.geojson = '{ "type": "MultiPolygon", "coordinates": [1.23, 4.56] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a two-dimensional array of coordinates" do
|
||||||
|
record.geojson = '{ "type": "MultiPolygon", "coordinates": [[1.23, 4.56], [7.89, 4.56]] }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a three-dimensional polygon coordinates array" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiPolygon",
|
||||||
|
"coordinates": [[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with a valid polygon" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiPolygon",
|
||||||
|
"coordinates": [[[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with multiple valid polygons" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiPolygon",
|
||||||
|
"coordinates": [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[100.0, 0.0],
|
||||||
|
[101.0, 0.0],
|
||||||
|
[101.0, 1.0],
|
||||||
|
[100.0, 1.0],
|
||||||
|
[100.0, 0.0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[100.8, 0.8],
|
||||||
|
[100.8, 0.2],
|
||||||
|
[100.2, 0.2],
|
||||||
|
[100.2, 0.8],
|
||||||
|
[100.8, 0.8]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with multiple polygons if some polygons are invalid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "MultiPolygon",
|
||||||
|
"coordinates": [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[1.23, 4.56],
|
||||||
|
[7.89, 10.11],
|
||||||
|
[12.13, 14.15],
|
||||||
|
[1.23, 4.56]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[100.0, 0.0],
|
||||||
|
[101.0, 0.0],
|
||||||
|
[101.0, 1.0],
|
||||||
|
[100.0, 1.0],
|
||||||
|
[100.0, 0.0]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[100.8, 0.8],
|
||||||
|
[100.8, 0.2],
|
||||||
|
[100.2, 0.2]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "GeometryCollection" do
|
||||||
|
it "is not valid if it doesn't contain geometries" do
|
||||||
|
record.geojson = '{ "type": "GeometryCollection" }'
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid if geometries is not an array" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "GeometryCollection",
|
||||||
|
"geometries": { "type": "Point", "coordinates": [1.23, 4.56] }
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if the array of geometries is empty" do
|
||||||
|
record.geojson = '{ "type": "GeometryCollection", "geometries": [] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if all geometries are valid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "GeometryCollection",
|
||||||
|
"geometries": [
|
||||||
|
{
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [100.0, 0.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": [
|
||||||
|
[101.0, 0.0],
|
||||||
|
[102.0, 1.0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid if some geometries are invalid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "GeometryCollection",
|
||||||
|
"geometries": [
|
||||||
|
{
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [100.0, 0.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": [101.0, 0.0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Feature" do
|
||||||
|
it "is valid with a valid geometry" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [1.23, 4.56]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid with a valid geometry" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [1.23]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "FeatureCollection" do
|
||||||
|
it "is not valid without features" do
|
||||||
|
record.geojson = '{ "type": "FeatureCollection" }'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid if features is not an array" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": {
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [1.23, 4.56]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if the array of features is empty" do
|
||||||
|
record.geojson = '{ "type": "FeatureCollection", "features": [] }'
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if all features are valid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [1.23, 4.56]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": [[101.0, 0.0], [102.0, 1.0]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not valid if some features are invalid" do
|
||||||
|
record.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [1.23, 4.56]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": [[101.0, 0.0], [102.0, 1.0]]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expect(record).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,10 +18,20 @@ describe Geozone do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "is not valid with invalid geojson file format" do
|
it "is not valid with invalid geojson file format" do
|
||||||
geozone.geojson = '{"geo\":{"type":"Incorrect key","coordinates": [
|
geozone.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Incorrect",
|
||||||
|
"coordinates": [
|
||||||
[40.8792937308316, -3.9259027239257],
|
[40.8792937308316, -3.9259027239257],
|
||||||
[40.8788966596619, -3.9249047078766],
|
[40.8788966596619, -3.9249047078766],
|
||||||
[40.8789131852224, -3.9247799675785]]}}'
|
[40.8789131852224, -3.9247799675785]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
expect(geozone).not_to be_valid
|
expect(geozone).not_to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -54,99 +64,153 @@ describe Geozone do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "#outline_points" do
|
describe "#outline_points" do
|
||||||
it "returns empty array when geojson is nil" do
|
it "returns nil when geojson is nil" do
|
||||||
expect(geozone.outline_points).to eq([])
|
geozone.geojson = nil
|
||||||
|
|
||||||
|
expect(geozone.outline_points).to be nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns coordinates array when geojson is not nil" do
|
it "returns normalized feature collection when geojson is a valid FeatureCollection" do
|
||||||
geozone = build(:geozone, geojson: '{
|
geozone.geojson = <<~JSON
|
||||||
"geometry": {
|
{
|
||||||
"type": "Polygon",
|
"type": "FeatureCollection",
|
||||||
"coordinates": [
|
"features": [{
|
||||||
[40.8792937308316, -3.9259027239257],
|
"type": "Feature",
|
||||||
[40.8788966596619, -3.9249047078766],
|
|
||||||
[40.8789131852224, -3.9247799675785]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
|
|
||||||
expect(geozone.outline_points).to eq(
|
|
||||||
[[-3.9259027239257, 40.8792937308316],
|
|
||||||
[-3.9249047078766, 40.8788966596619],
|
|
||||||
[-3.9247799675785, 40.8789131852224]]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "handles coordinates with three-dimensional arrays" do
|
|
||||||
geozone = build(:geozone, geojson: '{
|
|
||||||
"geometry": {
|
|
||||||
"type": "Polygon",
|
|
||||||
"coordinates": [[[40.8792937308316, -3.9259027239257],
|
|
||||||
[40.8788966596619, -3.9249047078766],
|
|
||||||
[40.8789131852224, -3.9247799675785]]]
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
|
|
||||||
expect(geozone.outline_points).to eq(
|
|
||||||
[[-3.9259027239257, 40.8792937308316],
|
|
||||||
[-3.9249047078766, 40.8788966596619],
|
|
||||||
[-3.9247799675785, 40.8789131852224]]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "handles coordinates with three-dimensional arrays with spaces between brackets" do
|
|
||||||
geozone = build(:geozone, geojson: '{
|
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[
|
"coordinates": [[
|
||||||
[40.8792937308316, -3.9259027239257],
|
[-3.9259027239257, 40.8792937308316],
|
||||||
[40.8788966596619, -3.9249047078766],
|
[-3.9249047078766, 40.8788966596619],
|
||||||
[40.8789131852224, -3.9247799675785]
|
[-3.9247799675785, 40.8789131852224],
|
||||||
|
[-3.9259027239257, 40.8792937308316]
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
}')
|
}]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
expect(geozone.outline_points).to eq(
|
expected = {
|
||||||
[[-3.9259027239257, 40.8792937308316],
|
type: "FeatureCollection",
|
||||||
|
features: [{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Polygon",
|
||||||
|
coordinates: [[
|
||||||
|
[-3.9259027239257, 40.8792937308316],
|
||||||
[-3.9249047078766, 40.8788966596619],
|
[-3.9249047078766, 40.8788966596619],
|
||||||
[-3.9247799675785, 40.8789131852224]]
|
[-3.9247799675785, 40.8789131852224],
|
||||||
)
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
},
|
||||||
|
properties: {}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(geozone.outline_points).to eq expected.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles coordinates with four-dimensional arrays" do
|
it "returns normalized feature collection when geojson is a valid Feature" do
|
||||||
geozone = build(:geozone, geojson: '{
|
geozone.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[[[40.8792937308316, -3.9259027239257],
|
"coordinates": [[
|
||||||
[40.8788966596619, -3.9249047078766],
|
[-3.9259027239257, 40.8792937308316],
|
||||||
[40.8789131852224, -3.9247799675785]]]]
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
|
|
||||||
expect(geozone.outline_points).to eq(
|
|
||||||
[[-3.9259027239257, 40.8792937308316],
|
|
||||||
[-3.9249047078766, 40.8788966596619],
|
[-3.9249047078766, 40.8788966596619],
|
||||||
[-3.9247799675785, 40.8789131852224]]
|
[-3.9247799675785, 40.8789131852224],
|
||||||
)
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: [{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Polygon",
|
||||||
|
coordinates: [[
|
||||||
|
[-3.9259027239257, 40.8792937308316],
|
||||||
|
[-3.9249047078766, 40.8788966596619],
|
||||||
|
[-3.9247799675785, 40.8789131852224],
|
||||||
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
},
|
||||||
|
properties: {}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(geozone.outline_points).to eq expected.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles coordinates with four-dimensional arrays with spaces between brackets" do
|
it "returns normalized feature collection when geojson is a valid Geometry object" do
|
||||||
geozone = build(:geozone, geojson: '{
|
geozone.geojson = <<~JSON
|
||||||
|
{
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[[
|
"coordinates": [[
|
||||||
[40.8792937308316, -3.9259027239257],
|
[-3.9259027239257, 40.8792937308316],
|
||||||
[40.8788966596619, -3.9249047078766],
|
|
||||||
[40.8789131852224, -3.9247799675785]
|
|
||||||
]]]
|
|
||||||
}
|
|
||||||
}')
|
|
||||||
|
|
||||||
expect(geozone.outline_points).to eq(
|
|
||||||
[[-3.9259027239257, 40.8792937308316],
|
|
||||||
[-3.9249047078766, 40.8788966596619],
|
[-3.9249047078766, 40.8788966596619],
|
||||||
[-3.9247799675785, 40.8789131852224]]
|
[-3.9247799675785, 40.8789131852224],
|
||||||
)
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: [{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Polygon",
|
||||||
|
coordinates: [[
|
||||||
|
[-3.9259027239257, 40.8792937308316],
|
||||||
|
[-3.9249047078766, 40.8788966596619],
|
||||||
|
[-3.9247799675785, 40.8789131852224],
|
||||||
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
},
|
||||||
|
properties: {}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(geozone.outline_points).to eq expected.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns normalized feature collection when geojson is a valid top-level Geometry object" do
|
||||||
|
geozone.geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[
|
||||||
|
[-3.9259027239257, 40.8792937308316],
|
||||||
|
[-3.9249047078766, 40.8788966596619],
|
||||||
|
[-3.9247799675785, 40.8789131852224],
|
||||||
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: [{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Polygon",
|
||||||
|
coordinates: [[
|
||||||
|
[-3.9259027239257, 40.8792937308316],
|
||||||
|
[-3.9249047078766, 40.8788966596619],
|
||||||
|
[-3.9247799675785, 40.8789131852224],
|
||||||
|
[-3.9259027239257, 40.8792937308316]
|
||||||
|
]]
|
||||||
|
},
|
||||||
|
properties: {}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(geozone.outline_points).to eq expected.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -110,8 +110,16 @@ describe "Admin geozones", :admin do
|
|||||||
|
|
||||||
scenario "Show polygons when a heading is associated with a geozone" do
|
scenario "Show polygons when a heading is associated with a geozone" do
|
||||||
Setting["feature.map"] = true
|
Setting["feature.map"] = true
|
||||||
|
geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.4], [-0.3, 51.6], [-0.1, 51.5]]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
geojson = '{ "geometry": { "type": "Polygon", "coordinates": [[-0.1,51.5],[-0.2,51.4],[-0.3,51.6]] } }'
|
|
||||||
geozone = create(:geozone, name: "Polygon me!")
|
geozone = create(:geozone, name: "Polygon me!")
|
||||||
budget = create(:budget)
|
budget = create(:budget)
|
||||||
group = create(:budget_group, budget: budget)
|
group = create(:budget_group, budget: budget)
|
||||||
@@ -145,7 +153,16 @@ describe "Admin geozones", :admin do
|
|||||||
|
|
||||||
scenario "Show polygons on geozone admin view" do
|
scenario "Show polygons on geozone admin view" do
|
||||||
Setting["feature.map"] = true
|
Setting["feature.map"] = true
|
||||||
geojson = '{ "geometry": { "type": "Polygon", "coordinates": [[-0.1,51.5],[-0.2,51.4],[-0.3,51.6]] } }'
|
geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.4], [-0.3, 51.6], [-0.1, 51.5]]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
geozone = create(:geozone, name: "Polygon me!", geojson: geojson)
|
geozone = create(:geozone, name: "Polygon me!", geojson: geojson)
|
||||||
|
|
||||||
visit admin_geozones_path
|
visit admin_geozones_path
|
||||||
@@ -156,4 +173,76 @@ describe "Admin geozones", :admin do
|
|||||||
expect(page).to have_link "Polygon me!", href: edit_admin_geozone_path(geozone)
|
expect(page).to have_link "Polygon me!", href: edit_admin_geozone_path(geozone)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario "overwrites geozone data with features data" do
|
||||||
|
geojson = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.5], [-0.2, 51.6], [-0.1, 51.6], [-0.1, 51.5]]]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"color": "#ff5733",
|
||||||
|
"headings": ["Zone 1", "Test zone"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
create(:geozone, color: "#001122", geojson: geojson)
|
||||||
|
|
||||||
|
visit admin_geozones_path
|
||||||
|
|
||||||
|
expect(page).to have_css ".map-polygon[fill='#ff5733']"
|
||||||
|
expect(page).not_to have_css ".map-polygon[fill='#001122']"
|
||||||
|
expect(page).not_to have_content "Zone 1"
|
||||||
|
expect(page).not_to have_content "Test zone"
|
||||||
|
|
||||||
|
find(".map-polygon").click
|
||||||
|
|
||||||
|
expect(page).to have_content "Zone 1\nTest zone"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "includes a control to select which geozones to display" do
|
||||||
|
north = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.5], [-0.2, 51.6], [-0.1, 51.6], [-0.1, 51.5]]]
|
||||||
|
},
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
south = <<~JSON
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Polygon",
|
||||||
|
"coordinates": [[[-0.1, 51.45], [-0.2, 51.45], [-0.2, 51.35], [-0.1, 51.35], [-0.1, 51.45]]]
|
||||||
|
},
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
create(:geozone, name: "North", geojson: north)
|
||||||
|
create(:geozone, name: "South", geojson: south)
|
||||||
|
|
||||||
|
visit admin_geozones_path
|
||||||
|
|
||||||
|
within(".map-location") do
|
||||||
|
expect(page).to have_css ".map-polygon", count: 2
|
||||||
|
|
||||||
|
find(".leaflet-control-layers").click
|
||||||
|
uncheck "South"
|
||||||
|
|
||||||
|
expect(page).to have_css ".map-polygon", count: 1
|
||||||
|
|
||||||
|
find(".map-polygon").click
|
||||||
|
|
||||||
|
expect(page).to have_content "North"
|
||||||
|
expect(page).not_to have_content "South"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1658,18 +1658,20 @@ describe "Budget Investments" do
|
|||||||
scenario "Shows the polygon associated to the current heading" do
|
scenario "Shows the polygon associated to the current heading" do
|
||||||
triangle = <<~JSON
|
triangle = <<~JSON
|
||||||
{
|
{
|
||||||
|
"type": "Feature",
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[-0.1,51.5],[-0.2,51.4],[-0.3,51.6]]
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.4], [-0.3, 51.6], [-0.1, 51.5]]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSON
|
JSON
|
||||||
|
|
||||||
rectangle = <<~JSON
|
rectangle = <<~JSON
|
||||||
{
|
{
|
||||||
|
"type": "Feature",
|
||||||
"geometry": {
|
"geometry": {
|
||||||
"type": "Polygon",
|
"type": "Polygon",
|
||||||
"coordinates": [[-0.1,51.5],[-0.2,51.5],[-0.2,51.6],[-0.1,51.6]]
|
"coordinates": [[[-0.1, 51.5], [-0.2, 51.5], [-0.2, 51.6], [-0.1, 51.6], [-0.1, 51.5]]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSON
|
JSON
|
||||||
|
|||||||
Reference in New Issue
Block a user