We're reworking the format validation to correctly interpret feature collection, feature, and geometry, according to RFC 7946 [1]. Since Leaflet interprets GeoJSON format, we're rendering the GeoJSON as a layer instead of as a set of points. For that, we're normalizing the GeoJSON to make sure it contains either a Feature or a FeatureCollection. We're also adding the Leaflet images to the assets path so the markers used for point geometries are rendered correctly. Note we no longer allow a GeoJSON containing a geometry but not a defined type. Since there might be invalid GeoJSON in existing Consul Democracy databases, we're normalizing these existing geometry objects to be part of a feature object. We're also wrapping the outline points in a FeatureCollection object because most of the large GIS systems eg ArcGIS, QGIS export geojson as a complete FeatureCollection. [1] https://datatracker.ietf.org/doc/html/rfc7946 Co-authored-by: Javi Martín <javim@elretirao.net>
330 lines
8.2 KiB
Ruby
330 lines
8.2 KiB
Ruby
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 valid with a two-dimensional array including only one point" do
|
|
record.geojson = '{ "type": "LineString", "coordinates": [[1.23, 4.56]] }'
|
|
|
|
expect(record).to be_valid
|
|
|
|
record.geojson = '{ "type": "MultiPoint", "coordinates": [[1.23, 4.56]] }'
|
|
|
|
expect(record).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 "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
|