WebLevel1 (full translate)
<% a = CachedMapItem.translate_geographic_item_id( @geographic_item.id, 'AssertedDistribution', ['ne_states']) %>
@@ -82,6 +83,9 @@
Names from geographic areas: <%= geographic_area_link_list(@geographic_item.geographic_areas) -%>
Parents through geographic areas: <%= geographic_area_link_list(@geographic_item.parent_geographic_areas) -%>
+
Gazetteer
+
Name from gazetteer: <%= gazetteer_link(@geographic_item.gazetteers.where(project_id: sessions_current_project_id).first) -%>
+
Parents
Through geographic areas: <%= geographic_item_parent_nav_links(@geographic_item) -%>
diff --git a/config/initializers/constants/model/documents.rb b/config/initializers/constants/model/documents.rb
index e0b56a392a..66603b211c 100644
--- a/config/initializers/constants/model/documents.rb
+++ b/config/initializers/constants/model/documents.rb
@@ -6,18 +6,114 @@
{
group: '',
content_type: '',
- extensions: ['Any extension']
+ extensions: [
+ {
+ extension: 'Any extension',
+ content_type: ''
+ }
+ ]
},
+
{
group: 'pdf',
- content_type: 'application/pdf',
- extensions: ['.pdf']
+ extensions: [
+ {
+ extension: '.pdf',
+ content_type: 'application/pdf',
+ }
+ ]
},
+
{
group: 'nexus',
- content_type: 'text/plain',
- extensions: ['.nex', '.nxs']
+ extensions: [
+ {
+ extension: '.nex',
+ content_type: 'text/plain'
+ },
+ {
+ extension: '.nxs',
+ content_type: 'text/plain'
+ }
+ ]
+ },
+
+ {
+ group: 'shapefile',
+ extensions: [
+ {
+ extension: '.shp',
+ content_type: 'application/x-shapefile'
+ },
+ {
+ extension: '.shx',
+ content_type: 'application/x-shapefile'
+ },
+ {
+ extension: '.dbf',
+ content_type: 'application/x-dbf'
+ },
+ {
+ extension: '.prj',
+ content_type: 'text/plain'
+ },
+ {
+ extension: '.cpg',
+ content_type: 'text/plain'
+ }
+ ]
+ },
+
+ {
+ group: 'shp',
+ extensions: [
+ {
+ extension: '.shp',
+ content_type: 'application/x-shapefile'
+ }
+ ]
+ },
+
+ {
+ group: 'shx',
+ extensions: [
+ {
+ extension: '.shx',
+ content_type: 'application/x-shapefile'
+ }
+ ]
+ },
+
+ {
+ group: 'dbf',
+ extensions: [
+ {
+ extension: '.dbf',
+ content_type: 'application/x-dbf'
+ }
+ ]
+ },
+
+ {
+ group: 'prj',
+ extensions: [
+ {
+ extension: '.prj',
+ content_type: 'text/plain'
+ }
+ ]
+ },
+
+ {
+ group: 'cpg',
+ extensions: [
+ {
+ extension: '.cpg',
+ content_type: 'text/plain'
+ }
+ ]
}
+
].freeze
end
diff --git a/config/initializers/vendor/paperclip.rb b/config/initializers/vendor/paperclip.rb
index e21adfd9eb..fd2e03bd41 100644
--- a/config/initializers/vendor/paperclip.rb
+++ b/config/initializers/vendor/paperclip.rb
@@ -4,5 +4,11 @@
Paperclip.options[:content_type_mappings] = {
ab1: %w(application/octet-stream),
nex: %w(text/plain),
- nxs: %w(text/plain)
+ nxs: %w(text/plain),
+ # Five shapefile formats:
+ shp: %w(application/octet-stream),
+ dbf: %w(application/x-dbf),
+ shx: %w(application/octet-stream),
+ prj: %w(text/plain),
+ cpg: %w(text/plain)
}
diff --git a/config/interface/hub/data.yml b/config/interface/hub/data.yml
index 452a94014b..302c40218e 100644
--- a/config/interface/hub/data.yml
+++ b/config/interface/hub/data.yml
@@ -176,6 +176,14 @@ Supporting:
- biology
- dna
- matrix
+ Gazetteer:
+ status: :prototype
+ related_models:
+ - GeographicArea
+ - Georeference
+ - CollectingEvent
+ categories:
+ - collecting_event
GeneAttribute:
status: :prototype
related_models:
@@ -202,6 +210,7 @@ Supporting:
hide: true
related_models:
- GeographicArea
+ - Gazetteer
- Georeference
- CollectingEvent
Image:
diff --git a/config/interface/hub/user_tasks.yml b/config/interface/hub/user_tasks.yml
index 25ed0454a3..c85f843975 100644
--- a/config/interface/hub/user_tasks.yml
+++ b/config/interface/hub/user_tasks.yml
@@ -956,9 +956,25 @@ filter_dwc_occurrences_task:
- collecting_event
- asserted_distribution
categories:
- - filter
+ - filters
status: prototype
description: 'Filter records in the DarwinCore index.'
+new_gazetteer_task:
+ hub: true
+ name: 'Create or edit Gazetteers'
+ related:
+ categories:
+ - collecting_event
+ - new
+ status: prototype
+ description: 'Create or edit named shapes for use in your project.'
+import_gazetteers_task:
+ hub: true
+ name: 'Import Gazetteers'
+ related:
+ categories:
+ status: prototype
+ description: 'Import Gazetteers from a shapefile.'
unify_objects_task:
hub: true
name: 'Unify objects'
diff --git a/config/interface/object_radials.yml b/config/interface/object_radials.yml
index 050f164ed4..0779f7982f 100644
--- a/config/interface/object_radials.yml
+++ b/config/interface/object_radials.yml
@@ -3,7 +3,7 @@
# The values here are returned as JSON via /metadata/radial/:klass
#
# Valid attributes (* required)
-# tasks: - A named task (see user_tasks.yml) !! Limit this to 3 or 4
+# tasks*: - A named task (see user_tasks.yml) !! Limit this to 3 or 4
# recent: - defaults to true if not provided, if false no recent list is shown
# edit: over-ride data->edit
# new: over-ride data->new
@@ -111,6 +111,12 @@ FieldOccurrence:
new: new_field_occurrence_task
config:
recent: true
+Gazetteer:
+ tasks:
+ - new_gazetteer_task
+ - import_gazetteers_task
+ new: new_gazetteer_task
+ edit: new_gazetteer_task
GeographicArea:
tasks:
- filter_asserted_distributions_task
diff --git a/config/routes/data.rb b/config/routes/data.rb
index 6f7400afd3..0f66c9207b 100644
--- a/config/routes/data.rb
+++ b/config/routes/data.rb
@@ -332,6 +332,21 @@
concerns [:data_routes]
end
+resources :gazetteer_imports, only: [:destroy], defaults: { format: :json } do
+ collection do
+ get :all, defaults: {format: :json}
+ end
+end
+
+resources :gazetteers do
+ concerns [:data_routes]
+ collection do
+ post :import, defaults: {format: :json}
+ post :preview, defaults: {format: :json} # post to support long WKT strings
+ get :shapefile_fields, default: {format: :json}
+ end
+end
+
resources :geographic_areas, only: [:index, :show] do
collection do
get 'download'
diff --git a/config/routes/tasks.rb b/config/routes/tasks.rb
index 0931b8ca33..d05c512718 100644
--- a/config/routes/tasks.rb
+++ b/config/routes/tasks.rb
@@ -1,4 +1,14 @@
scope :tasks do
+ scope :gazetteers do
+ scope :import_gazetteers, controller: 'tasks/gazetteers/import_gazetteers' do
+ get '/', action: :index, as: 'import_gazetteers_task'
+ end
+
+ scope :new_gazetteer, controller: 'tasks/gazetteers/new_gazetteer' do
+ get '/', action: :index, as: 'new_gazetteer_task'
+ end
+ end
+
scope :controlled_vocabulary_terms do
scope :projects_summary, controller: 'tasks/controlled_vocabulary_terms/projects_summary' do
get '/', action: :index, as: 'summarize_projects_controlled_vocabulary_terms_task'
diff --git a/db/migrate/20220927154908_add_cached_total_area_to_geographic_item.rb b/db/migrate/20220927154908_add_cached_total_area_to_geographic_item.rb
index b0e82bdb93..7d6dbdb0e4 100644
--- a/db/migrate/20220927154908_add_cached_total_area_to_geographic_item.rb
+++ b/db/migrate/20220927154908_add_cached_total_area_to_geographic_item.rb
@@ -11,10 +11,12 @@ def change
# begin
GeographicItem.where.not(type: 'GeographicItem::Point').find_each do |i|
- r = i.send(:set_cached)
+ i.send(:set_cached)
end
- GeographicItem::Point.update_all(cached_total_area: 0.0)
+
+ GeographicItem.where(type: 'GeographicItem::Point')
+ .update_all(cached_total_area: 0.0)
# rescue => exception
# puts "Error - id: #{i.id}"
diff --git a/db/migrate/20240604024742_create_gazetteers.rb b/db/migrate/20240604024742_create_gazetteers.rb
new file mode 100644
index 0000000000..94b059cf6c
--- /dev/null
+++ b/db/migrate/20240604024742_create_gazetteers.rb
@@ -0,0 +1,18 @@
+class CreateGazetteers < ActiveRecord::Migration[7.1]
+ def change
+ create_table :gazetteers do |t|
+
+ t.integer :geographic_item_id, null: false, index: true
+ t.integer :parent_id, index: true
+ t.string :name, null: false, index: true
+ t.string :iso_3166_a2, index: true
+ t.string :iso_3166_a3, index: true
+ t.references :project, index: true, foreign_key: true
+
+ t.timestamps null: false
+
+ t.integer :created_by_id, null: false, index: true
+ t.integer :updated_by_id, null: false, index: true
+ end
+ end
+end
diff --git a/db/migrate/20240604160009_add_geography_to_geometric_item.rb b/db/migrate/20240604160009_add_geography_to_geometric_item.rb
new file mode 100644
index 0000000000..579269664b
--- /dev/null
+++ b/db/migrate/20240604160009_add_geography_to_geometric_item.rb
@@ -0,0 +1,6 @@
+class AddGeographyToGeometricItem < ActiveRecord::Migration[7.1]
+ def change
+ add_column :geographic_items, :geography, :geometry, geographic: true, has_z: true
+ add_index :geographic_items, :geography, using: :gist
+ end
+end
diff --git a/db/migrate/20240609040400_create_gazetteer_hierarchies.rb b/db/migrate/20240609040400_create_gazetteer_hierarchies.rb
new file mode 100644
index 0000000000..48cfc32fd1
--- /dev/null
+++ b/db/migrate/20240609040400_create_gazetteer_hierarchies.rb
@@ -0,0 +1,16 @@
+class CreateGazetteerHierarchies < ActiveRecord::Migration[7.1]
+ def change
+ create_table :gazetteer_hierarchies, id: false do |t|
+ t.integer :ancestor_id, null: false
+ t.integer :descendant_id, null: false
+ t.integer :generations, null: false
+ end
+
+ add_index :gazetteer_hierarchies, [:ancestor_id, :descendant_id, :generations],
+ unique: true,
+ name: "gazetteer_anc_desc_idx"
+
+ add_index :gazetteer_hierarchies, [:descendant_id],
+ name: "gazetteer_desc_idx"
+ end
+end
diff --git a/db/migrate/20240801024223_copy_gicolumns_to_geography_column.rb b/db/migrate/20240801024223_copy_gicolumns_to_geography_column.rb
new file mode 100644
index 0000000000..d43c20552b
--- /dev/null
+++ b/db/migrate/20240801024223_copy_gicolumns_to_geography_column.rb
@@ -0,0 +1,19 @@
+class CopyGicolumnsToGeographyColumn < ActiveRecord::Migration[7.1]
+ def change
+
+ ApplicationRecord.connection.execute(
+ "UPDATE geographic_items SET geography=(
+ CASE geographic_items.type
+ WHEN 'GeographicItem::MultiPolygon' THEN multi_polygon
+ WHEN 'GeographicItem::Point' THEN point
+ WHEN 'GeographicItem::LineString' THEN line_string
+ WHEN 'GeographicItem::Polygon' THEN polygon
+ WHEN 'GeographicItem::MultiLineString' THEN multi_line_string
+ WHEN 'GeographicItem::MultiPoint' THEN multi_point
+ WHEN 'GeographicItem::GeometryCollection' THEN geometry_collection
+ END
+ ) WHERE geography IS NULL;"
+ )
+
+ end
+end
diff --git a/db/migrate/20240801031752_remove_type_from_geographic_items.rb b/db/migrate/20240801031752_remove_type_from_geographic_items.rb
new file mode 100644
index 0000000000..f60d955cc2
--- /dev/null
+++ b/db/migrate/20240801031752_remove_type_from_geographic_items.rb
@@ -0,0 +1,8 @@
+class RemoveTypeFromGeographicItems < ActiveRecord::Migration[7.1]
+ def change
+
+ # This migration was no-oped during development.
+ #remove_column :geographic_items, :type, :string
+
+ end
+end
diff --git a/db/migrate/20240906022857_create_gazetteer_imports.rb b/db/migrate/20240906022857_create_gazetteer_imports.rb
new file mode 100644
index 0000000000..53b0e4e7cf
--- /dev/null
+++ b/db/migrate/20240906022857_create_gazetteer_imports.rb
@@ -0,0 +1,19 @@
+class CreateGazetteerImports < ActiveRecord::Migration[7.1]
+ def change
+ create_table :gazetteer_imports do |t|
+
+ t.string :shapefile
+ t.integer :num_records
+ t.integer :num_records_processed
+ t.string :aborted_reason
+ t.datetime :started_at
+ t.datetime :ended_at
+
+ t.references :project, index: true, foreign_key: true
+ t.timestamps null: false
+
+ t.integer :created_by_id, null: false, index: true
+ t.integer :updated_by_id, null: false, index: true
+ end
+ end
+end
diff --git a/db/migrate/20241011180040_drop_gazetteer_hierarchies_table.rb b/db/migrate/20241011180040_drop_gazetteer_hierarchies_table.rb
new file mode 100644
index 0000000000..ed6e88b368
--- /dev/null
+++ b/db/migrate/20241011180040_drop_gazetteer_hierarchies_table.rb
@@ -0,0 +1,5 @@
+class DropGazetteerHierarchiesTable < ActiveRecord::Migration[7.1]
+ def change
+ drop_table :gazetteer_hierarchies
+ end
+end
diff --git a/db/migrate/20241024032217_add_project_names_to_gazetteer_import.rb b/db/migrate/20241024032217_add_project_names_to_gazetteer_import.rb
new file mode 100644
index 0000000000..afe2f15af1
--- /dev/null
+++ b/db/migrate/20241024032217_add_project_names_to_gazetteer_import.rb
@@ -0,0 +1,5 @@
+class AddProjectNamesToGazetteerImport < ActiveRecord::Migration[7.1]
+ def change
+ add_column :gazetteer_imports, :project_names, :string
+ end
+end
diff --git a/db/migrate/20241025144619_rename_columns_in_gazetteer_import.rb b/db/migrate/20241025144619_rename_columns_in_gazetteer_import.rb
new file mode 100644
index 0000000000..7c770ccf15
--- /dev/null
+++ b/db/migrate/20241025144619_rename_columns_in_gazetteer_import.rb
@@ -0,0 +1,6 @@
+class RenameColumnsInGazetteerImport < ActiveRecord::Migration[7.1]
+ def change
+ rename_column :gazetteer_imports, :aborted_reason, :error_messages
+ rename_column :gazetteer_imports, :num_records_processed, :num_records_imported
+ end
+end
diff --git a/db/migrate/20241027160558_remove_parent_id_from_gazetteer.rb b/db/migrate/20241027160558_remove_parent_id_from_gazetteer.rb
new file mode 100644
index 0000000000..866a9e0e69
--- /dev/null
+++ b/db/migrate/20241027160558_remove_parent_id_from_gazetteer.rb
@@ -0,0 +1,5 @@
+class RemoveParentIdFromGazetteer < ActiveRecord::Migration[7.1]
+ def change
+ remove_column :gazetteers, :parent_id, :integer
+ end
+end
diff --git a/db/migrate/20241113175602_remove_null_constraint_on_geographic_item_type.rb b/db/migrate/20241113175602_remove_null_constraint_on_geographic_item_type.rb
new file mode 100644
index 0000000000..6dda9cfb7f
--- /dev/null
+++ b/db/migrate/20241113175602_remove_null_constraint_on_geographic_item_type.rb
@@ -0,0 +1,5 @@
+class RemoveNullConstraintOnGeographicItemType < ActiveRecord::Migration[7.2]
+ def change
+ change_column_null :geographic_items, :type, true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1b33e904bb..90d9d29aee 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -955,6 +955,43 @@
t.index ["updated_by_id"], name: "index_field_occurrences_on_updated_by_id"
end
+ create_table "gazetteer_imports", force: :cascade do |t|
+ t.string "shapefile"
+ t.integer "num_records"
+ t.integer "num_records_imported"
+ t.string "error_messages"
+ t.datetime "started_at"
+ t.datetime "ended_at"
+ t.bigint "project_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "created_by_id", null: false
+ t.integer "updated_by_id", null: false
+ t.string "project_names"
+ t.index ["created_by_id"], name: "index_gazetteer_imports_on_created_by_id"
+ t.index ["project_id"], name: "index_gazetteer_imports_on_project_id"
+ t.index ["updated_by_id"], name: "index_gazetteer_imports_on_updated_by_id"
+ end
+
+ create_table "gazetteers", force: :cascade do |t|
+ t.integer "geographic_item_id", null: false
+ t.string "name", null: false
+ t.string "iso_3166_a2"
+ t.string "iso_3166_a3"
+ t.bigint "project_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "created_by_id", null: false
+ t.integer "updated_by_id", null: false
+ t.index ["created_by_id"], name: "index_gazetteers_on_created_by_id"
+ t.index ["geographic_item_id"], name: "index_gazetteers_on_geographic_item_id"
+ t.index ["iso_3166_a2"], name: "index_gazetteers_on_iso_3166_a2"
+ t.index ["iso_3166_a3"], name: "index_gazetteers_on_iso_3166_a3"
+ t.index ["name"], name: "index_gazetteers_on_name"
+ t.index ["project_id"], name: "index_gazetteers_on_project_id"
+ t.index ["updated_by_id"], name: "index_gazetteers_on_updated_by_id"
+ end
+
create_table "gene_attributes", id: :serial, force: :cascade do |t|
t.integer "descriptor_id", null: false
t.integer "sequence_id", null: false
@@ -1041,10 +1078,12 @@
t.geography "geometry_collection", limit: {:srid=>4326, :type=>"geometry_collection", :has_z=>true, :geographic=>true}
t.integer "created_by_id", null: false
t.integer "updated_by_id", null: false
- t.string "type", null: false
+ t.string "type"
t.decimal "cached_total_area"
+ t.geography "geography", limit: {:srid=>4326, :type=>"geometry", :has_z=>true, :geographic=>true}
t.index "st_centroid(\nCASE type\n WHEN 'GeographicItem::MultiPolygon'::text THEN (multi_polygon)::geometry\n WHEN 'GeographicItem::Point'::text THEN (point)::geometry\n WHEN 'GeographicItem::LineString'::text THEN (line_string)::geometry\n WHEN 'GeographicItem::Polygon'::text THEN (polygon)::geometry\n WHEN 'GeographicItem::MultiLineString'::text THEN (multi_line_string)::geometry\n WHEN 'GeographicItem::MultiPoint'::text THEN (multi_point)::geometry\n WHEN 'GeographicItem::GeometryCollection'::text THEN (geometry_collection)::geometry\n ELSE NULL::geometry\nEND)", name: "idx_centroid", using: :gist
t.index ["created_by_id"], name: "index_geographic_items_on_created_by_id"
+ t.index ["geography"], name: "index_geographic_items_on_geography", using: :gist
t.index ["geometry_collection"], name: "geometry_collection_gix", using: :gist
t.index ["line_string"], name: "line_string_gix", using: :gist
t.index ["multi_line_string"], name: "multi_line_string_gix", using: :gist
@@ -2292,6 +2331,8 @@
add_foreign_key "field_occurrences", "ranged_lot_categories"
add_foreign_key "field_occurrences", "users", column: "created_by_id"
add_foreign_key "field_occurrences", "users", column: "updated_by_id"
+ add_foreign_key "gazetteer_imports", "projects"
+ add_foreign_key "gazetteers", "projects"
add_foreign_key "gene_attributes", "controlled_vocabulary_terms"
add_foreign_key "gene_attributes", "projects"
add_foreign_key "gene_attributes", "sequences"
diff --git a/exe/delayed_job b/exe/delayed_job
index 5e3ef84154..e640da7105 100755
--- a/exe/delayed_job
+++ b/exe/delayed_job
@@ -2,4 +2,4 @@
set -e
cd /app
-exec chpst -u app env QUEUE=project_download,cached_map,dwca_export,coldp_export,basic_nomenclature_export,dwc_occurrence_index,dwca_build_index,import_dataset_import,import_dataset_stage,cache,query_batch_update,import_nexus /usr/bin/bundle exec rails jobs:work
+exec chpst -u app env QUEUE=project_download,cached_map,dwca_export,coldp_export,basic_nomenclature_export,dwc_occurrence_index,dwca_build_index,import_dataset_import,import_dataset_stage,cache,query_batch_update,import_nexus,import_gazetteers /usr/bin/bundle exec rails jobs:work
diff --git a/lib/gis/geo_json.rb b/lib/gis/geo_json.rb
index b16fd7d0ee..24e070d262 100644
--- a/lib/gis/geo_json.rb
+++ b/lib/gis/geo_json.rb
@@ -62,25 +62,9 @@ def self.feature(object)
result
end
- # @return String, nil
- # a GeoJSON string
- # The point is to not instantiate a whole AR object, but query
- # as directly as possible to get the GeoJSON string value.
- # It's unclear as to whether this saves that much time.
- def self.quick_geo_json_string(geographic_item_id)
- return nil if geographic_item_id.nil?
-
- query = "ST_AsGeoJSON(#{GeographicItem::GEOMETRY_SQL.to_sql}::geometry) geo_json_str"
- a = GeographicItem.where(id: geographic_item_id)
- .select(query)
- .limit(1)
- ::GeographicItem.connection.select_all(a.to_sql)
- .first['geo_json_str']
- end
-
# @return [GeoJSON] content for geometry
def self.quick_geo_json(geographic_item_id)
- JSON.parse(quick_geo_json_string(geographic_item_id))
+ GeographicItem.find(geographic_item_id).to_geo_json
end
# # @return [a Feature]
diff --git a/lib/queries/asserted_distribution/filter.rb b/lib/queries/asserted_distribution/filter.rb
index 85969bb909..6a2b4a973c 100644
--- a/lib/queries/asserted_distribution/filter.rb
+++ b/lib/queries/asserted_distribution/filter.rb
@@ -7,12 +7,14 @@ class Filter < Query::Filter
include Queries::Concerns::Notes
include Queries::Concerns::DataAttributes
include Queries::Concerns::Citations
+ include Queries::Concerns::Gazetteers
include Queries::Concerns::Confidences
PARAMS = [
:asserted_distribution_id,
:descendants,
:geo_json,
+ :gazetteer_id,
:geographic_area_id,
:geographic_item_id,
:geographic_area_mode,
@@ -22,6 +24,7 @@ class Filter < Query::Filter
:taxon_name_id,
:wkt,
asserted_distribution_id: [],
+ gazetteer_id: [],
geographic_area_id: [],
geographic_item_id: [],
otu_id: [],
@@ -93,6 +96,7 @@ def initialize(query_params)
set_confidences_params(params)
set_citations_params(params)
set_data_attributes_params(params)
+ set_gazetteer_params(params)
set_notes_params(params)
set_tags_params(params)
end
@@ -132,10 +136,10 @@ def wkt_facet
end
def from_wkt(wkt_shape)
- i = ::GeographicItem.joins(:geographic_areas).where(::GeographicItem.contained_by_wkt_sql(wkt_shape))
+ i = ::GeographicItem.joins(:geographic_areas).where(::GeographicItem.covered_by_wkt_sql(wkt_shape))
j = ::GeographicArea.joins(:geographic_items).where(geographic_items: i)
- k = ::GeographicArea.descendants_of(j) # Add children that might not be caught because they don't have a shapes
+ k = ::GeographicArea.descendants_of(j) # Add children that might not be caught because they don't have shapes
l = ::GeographicArea.from("((#{j.to_sql}) UNION (#{k.to_sql})) as geographic_areas").distinct
@@ -172,9 +176,15 @@ def spatial_query
if geometry = RGeo::GeoJSON.decode(geo_json)
case geometry.geometry_type.to_s
when 'Point'
- ::GeographicItem.joins(:geographic_areas).where( ::GeographicItem.within_radius_of_wkt_sql(geometry.to_s, radius ) )
+ ::GeographicItem
+ .joins(:geographic_areas)
+ .where(
+ ::GeographicItem.within_radius_of_wkt_sql(geometry.to_s, radius)
+ )
when 'Polygon', 'MultiPolygon'
- ::GeographicItem.joins(:geographic_areas).where(::GeographicItem.contained_by_wkt_sql(geometry.to_s))
+ ::GeographicItem
+ .joins(:geographic_areas)
+ .where(::GeographicItem.covered_by_wkt_sql(geometry.to_s))
else
nil
end
@@ -202,7 +212,9 @@ def geographic_area_id_facet
b = ::AssertedDistribution.where(geographic_area: a)
when true # spatial
i = ::GeographicItem.joins(:geographic_areas).where(geographic_areas: a) # .unscope
- wkt_shape = ::GeographicItem.st_union(i).to_a.first['collection'].to_s # todo, check
+ wkt_shape =
+ ::Queries::GeographicItem.st_union(i)
+ .to_a.first['st_union'].to_s # todo, check
return from_wkt(wkt_shape)
end
@@ -297,6 +309,7 @@ def merge_clauses
otu_query_facet,
taxon_name_query_facet,
+ gazetteer_id_facet,
geographic_area_id_facet,
geographic_item_id_facet,
taxon_name_id_facet,
diff --git a/lib/queries/collecting_event/filter.rb b/lib/queries/collecting_event/filter.rb
index 34efda71ca..989c653354 100644
--- a/lib/queries/collecting_event/filter.rb
+++ b/lib/queries/collecting_event/filter.rb
@@ -13,6 +13,7 @@ class Filter < Query::Filter
include Queries::Concerns::DataAttributes
include Queries::Concerns::DateRanges
include Queries::Concerns::Depictions
+ include Queries::Concerns::Gazetteers
include Queries::Concerns::Notes
include Queries::Concerns::Protocols
include Queries::Concerns::Tags
@@ -56,9 +57,11 @@ class Filter < Query::Filter
PARAMS = [
*BASE_PARAMS,
+ :gazetteer_id,
:otu_id,
:collection_object_id,
collection_object_id: [],
+ gazetteer_id: [],
otu_id: [],
].inject([{}]){|ary, k| k.is_a?(Hash) ? ary.last.merge!(k) : ary.unshift(k); ary}.freeze
@@ -175,6 +178,7 @@ def initialize(query_params)
set_data_attributes_params(params)
set_date_params(params)
set_depiction_params(params)
+ set_gazetteer_params(params)
set_notes_params(params)
set_protocols_params(params)
set_tags_params(params)
@@ -236,13 +240,14 @@ def geographic_area_id_facet
a = ::GeographicArea.descendants_of_any(geographic_area_id)
end
- b = nil
case geographic_area_mode
when nil, false # exact, descendants
return ::CollectingEvent.where(geographic_area: a)
when true # spatial
i = ::GeographicItem.joins(:geographic_areas).where(geographic_areas: a) # .unscope
- wkt_shape = ::GeographicItem.st_union(i).to_a.first['collection'].to_s
+ wkt_shape =
+ ::Queries::GeographicItem.st_union(i)
+ .to_a.first['st_union'].to_s
return from_wkt(wkt_shape)
end
end
@@ -323,11 +328,11 @@ def spatial_query(geometry_type, wkt)
when 'Point'
::CollectingEvent
.joins(:geographic_items)
- .where(::GeographicItem.within_radius_of_wkt_sql(wkt, radius ))
+ .where(::GeographicItem.within_radius_of_wkt_sql(wkt, radius))
when 'Polygon', 'MultiPolygon'
::CollectingEvent
.joins(:geographic_items)
- .where(::GeographicItem.contained_by_wkt_sql(wkt))
+ .where(::GeographicItem.covered_by_wkt_sql(wkt))
else
nil
end
@@ -458,6 +463,7 @@ def merge_clauses
collection_objects_facet,
collector_id_facet,
geo_json_facet,
+ gazetteer_id_facet,
geographic_area_facet,
geographic_area_id_facet,
georeferences_facet,
diff --git a/lib/queries/common_name/filter.rb b/lib/queries/common_name/filter.rb
index 8ed9633feb..5077566935 100644
--- a/lib/queries/common_name/filter.rb
+++ b/lib/queries/common_name/filter.rb
@@ -4,6 +4,7 @@ class Filter < Query::Filter
PARAMS = [
:common_name_id,
+ :gazetteer_id,
:geographic_area_id,
:language_id,
:name,
@@ -13,10 +14,12 @@ class Filter < Query::Filter
].freeze
# Query variables
- attr_accessor :name, :geographic_area_id, :otu_id, :language_id
+ attr_accessor :name, :gazetteer_id, :geographic_area_id, :otu_id,
+ :language_id
def initialize(query_params)
super
+ @gazetteer_id = params[:gazetteer_id]
@geographic_area_id = params[:geographic_area_id]
@language_id = params[:language_id]
@name = params[:name]
@@ -31,6 +34,10 @@ def language_id
[@language_id].flatten.compact
end
+ def gazetteer_id
+ [@gazetteer_id].flatten.compact
+ end
+
def geographic_area_id
[@geographic_area_id_id].flatten.compact
end
@@ -54,6 +61,11 @@ def name_facet
table[:name].eq(name)
end
+ def gazetteer_id_facet
+ return nil if gazetteer_id.empty?
+ table[:gazetteer_id].in(gazetteer_id)
+ end
+
def geographic_area_id_facet
return nil if geographic_area_id.empty?
table[:geographic_area_id].in(geographic_area_id)
@@ -61,6 +73,7 @@ def geographic_area_id_facet
def and_clauses
[
+ gazetteer_id_facet,
geographic_area_id_facet,
language_id_facet,
name_facet,
diff --git a/lib/queries/concerns/gazetteers.rb b/lib/queries/concerns/gazetteers.rb
new file mode 100644
index 0000000000..a23300a84f
--- /dev/null
+++ b/lib/queries/concerns/gazetteers.rb
@@ -0,0 +1,36 @@
+require_dependency Rails.root.to_s + '/lib/queries/geographic_item/filter.rb'
+
+# Helpers for gazetteer-related queries
+#
+# !! You must have `#from_wkt` defined in the module to use this concern
+# !! You must call set_gazetteer_params in initialize()
+#
+# Concern specs are in
+# spec/lib/queries/source/filter_spec.rb
+module Queries::Concerns::Gazetteers
+ extend ActiveSupport::Concern
+
+ included do
+ # @param gazetteer_id [Array, Integer, String]
+ # @return [Array]
+ attr_accessor :gazetteer_id
+
+ def gazetteer_id
+ [@gazetteer_id].flatten.compact
+ end
+ end
+
+ def set_gazetteer_params(params)
+ @gazetteer_id = params[:gazetteer_id]
+ end
+
+ def gazetteer_id_facet
+ return nil if gazetteer_id.empty?
+
+ i = ::GeographicItem.joins(:gazetteers).where(gazetteers: { id: gazetteer_id })
+ wkt_shape = ::Queries::GeographicItem.st_union(i).to_a.first['st_union'].to_s
+
+ from_wkt(wkt_shape)
+ end
+
+end
diff --git a/lib/queries/document/autocomplete.rb b/lib/queries/document/autocomplete.rb
index c703c12200..7aafb5e403 100644
--- a/lib/queries/document/autocomplete.rb
+++ b/lib/queries/document/autocomplete.rb
@@ -28,7 +28,11 @@ def file_name_like
# @return [Scope]
def all
- ::Document.where(where_sql).limit(50).order(:id)
+ ::Document
+ .with_project_id(project_id)
+ .where(where_sql)
+ .limit(50)
+ .order(:id)
end
end
diff --git a/lib/queries/document/filter.rb b/lib/queries/document/filter.rb
index 9e421278bd..50a1d05b25 100644
--- a/lib/queries/document/filter.rb
+++ b/lib/queries/document/filter.rb
@@ -4,19 +4,19 @@ class Filter < Query::Filter
PARAMS = [
:document_id,
- :file_extension_group,
+ :file_extension_group_name,
document_id: [],
].freeze
attr_accessor :document_id
- attr_accessor :file_extension_group
+ attr_accessor :file_extension_group_name
def initialize(query_params)
super
@document_id = params[:document_id]
- @file_extension_group = params[:file_extension_group]
+ @file_extension_group_name = params[:file_extension_group_name]
end
def document_id
@@ -24,43 +24,40 @@ def document_id
end
def file_extension_facet
- return nil if !file_extension_group&.present?
+ return nil if !file_extension_group_name&.present?
d = FILE_EXTENSIONS_DATA.find { |g|
- g[:group] == file_extension_group
+ g[:group] == file_extension_group_name
}
d ||= {
group: '',
- content_type: '', # matches no document
- extensions: ['']
+ extensions: [
+ {
+ extension: '',
+ content_type: '' # matches no document
+ }
+ ]
}
- type_match = table[:document_file_content_type].eq(d[:content_type])
+ a = []
+ d[:extensions].each { |e|
+ a <<
+ table[:document_file_content_type].eq(e[:content_type])
+ .and(table[:document_file_file_name].matches('%' + e[:extension]))
+ }
- extensions_match = extension_matches_ored(d[:extensions])
+ q = a.shift
+ a.each do |b|
+ q = q.or(b)
+ end
- type_match.and(extensions_match)
+ q
end
def and_clauses
[file_extension_facet]
end
-
- private
-
- def extension_matches_ored(exts)
- clauses = exts.map { |ext|
- table[:document_file_file_name].matches('%' + ext)
- }
-
- a = clauses.shift
- clauses.each do |b|
- a = a.or(b)
- end
-
- a
- end
end
end
end
\ No newline at end of file
diff --git a/lib/queries/gazetteer/autocomplete.rb b/lib/queries/gazetteer/autocomplete.rb
new file mode 100644
index 0000000000..d7637c1bf6
--- /dev/null
+++ b/lib/queries/gazetteer/autocomplete.rb
@@ -0,0 +1,61 @@
+module Queries
+ module Gazetteer
+ class Autocomplete < Query::Autocomplete
+
+ include Queries::Concerns::AlternateValues
+
+ def initialize(string, **params)
+ set_alternate_value(params)
+ super
+ end
+
+ def autocomplete_iso_a2
+ return nil if query_string.length != 2
+ ::Gazetteer.where('iso_3166_a2 ILIKE ?', query_string)
+ end
+
+ def autocomplete_iso_a3
+ return nil if query_string.length != 3
+ ::Gazetteer.where('iso_3166_a3 ILIKE ?', query_string)
+ end
+
+ def updated_queries
+ queries = [
+ autocomplete_named,
+ autocomplete_exact_id,
+ matching_alternate_value_on(:name),
+ autocomplete_iso_a2,
+ autocomplete_iso_a3
+ ]
+
+ queries.compact!
+ return [] if queries.empty?
+
+ updated_queries = []
+
+ queries.each do |q|
+ a = q.where(project_id:) if project_id.present?
+ updated_queries << a
+ end
+
+ updated_queries
+ end
+
+ # @return [Array]
+ def autocomplete
+ queries = updated_queries
+
+ result = []
+
+ queries.each do |q|
+ result += q.to_a
+ result.uniq!
+ break if result.count > 19
+ end
+
+ result[0..19]
+ end
+
+ end
+ end
+end
diff --git a/lib/queries/geographic_area/filter.rb b/lib/queries/geographic_area/filter.rb
index 933108a517..e2c9529507 100644
--- a/lib/queries/geographic_area/filter.rb
+++ b/lib/queries/geographic_area/filter.rb
@@ -59,8 +59,26 @@ def name_facet
def containing_point_facet
return nil if containing_point.nil?
- a = ::GeographicArea.joins(:geographic_items).where("ST_Contains(polygon::geometry, GeomFromEWKT('srid=4326;#{containing_point}'))")
- b = ::GeographicArea.joins(:geographic_items).where("ST_Contains(multi_polygon::geometry, GeomFromEWKT('srid=4326;#{containing_point}'))")
+ a = ::GeographicArea
+ .joins(:geographic_items)
+ .merge(::GeographicItem.polygons)
+ .where(
+ ::GeographicItem.st_covers_sql(
+ ::GeographicItem.geography_as_geometry,
+ ::GeographicItem.st_geom_from_text_sql(containing_point)
+ )
+ )
+
+ b = ::GeographicArea
+ .joins(:geographic_items)
+ .merge(::GeographicItem.multi_polygons)
+ .where(
+ ::GeographicItem.st_covers_sql(
+ ::GeographicItem.geography_as_geometry,
+ ::GeographicItem.st_geom_from_text_sql(containing_point)
+ )
+ )
+
referenced_klass_union([a,b])
end
diff --git a/lib/queries/geographic_item/filter.rb b/lib/queries/geographic_item/filter.rb
new file mode 100644
index 0000000000..fae20b6f39
--- /dev/null
+++ b/lib/queries/geographic_item/filter.rb
@@ -0,0 +1,34 @@
+module Queries
+ module GeographicItem
+
+ def self.st_union(geographic_item_scope)
+ ::GeographicItem
+ .select(
+ ::GeographicItem.st_union_sql(::GeographicItem.geography_as_geometry)
+ )
+ .where(id: geographic_item_scope.pluck(:id))
+ end
+
+ class Filter < Query::Filter
+ PARAMS = [
+ :geographic_item_id
+ ].freeze
+
+ # @return Array
+ attr_accessor :geographic_item_id
+
+ # @param [Hash] params
+ def initialize(query_params)
+ super
+
+ @geographic_item_id = params[:geographic_item_id]
+ end
+
+ def geographic_item_id
+ [@geographic_item_id].flatten.compact
+ end
+
+ end
+
+ end
+end
diff --git a/lib/queries/otu/filter.rb b/lib/queries/otu/filter.rb
index 15851f82c9..5b54d540e6 100644
--- a/lib/queries/otu/filter.rb
+++ b/lib/queries/otu/filter.rb
@@ -4,6 +4,7 @@ class Filter < Query::Filter
include Queries::Concerns::Citations
include Queries::Concerns::DataAttributes
include Queries::Concerns::Depictions
+ include Queries::Concerns::Gazetteers
include Queries::Concerns::Tags
include Queries::Concerns::Notes
include Queries::Concerns::Confidences
@@ -22,6 +23,7 @@ class Filter < Query::Filter
:descendants,
:descriptor_id,
:geo_json,
+ :gazetteer_id,
:geographic_area_id,
:geographic_area_mode,
:name,
@@ -36,6 +38,7 @@ class Filter < Query::Filter
collecting_event_id: [],
descriptor_id: [],
+ gazetteer_id: [],
geographic_area_id: [],
name: [],
otu_id: [],
@@ -216,6 +219,7 @@ def initialize(query_params)
set_depiction_params(params)
set_data_attributes_params(params)
set_tags_params(params)
+ set_gazetteer_params(params)
end
def biological_associations_table
@@ -437,7 +441,9 @@ def geographic_area_id_facet
c = ::Otu.joins(collection_objects: [:collecting_event]).where(collecting_events: { geographic_area: a })
when true # spatial
i = ::GeographicItem.joins(:geographic_areas).where(geographic_areas: a) # .unscope
- wkt_shape = ::GeographicItem.st_union(i).to_a.first['collection'].to_s # todo, check
+ wkt_shape =
+ ::Queries::GeographicItem.st_union(i)
+ .to_a.first['st_union'].to_s # todo, check
return from_wkt(wkt_shape)
end
@@ -645,6 +651,7 @@ def merge_clauses
contents_facet,
descriptor_id_facet,
geo_json_facet,
+ gazetteer_id_facet,
geographic_area_id_facet,
observations_facet,
taxon_name_id_facet,
diff --git a/lib/queries/query/filter.rb b/lib/queries/query/filter.rb
index 2a58b9f8ae..d43690e37d 100644
--- a/lib/queries/query/filter.rb
+++ b/lib/queries/query/filter.rb
@@ -11,7 +11,7 @@ module Queries
# acceptable params, dynamically, based on the nature
# of the nested queries.
#
- # Test coverage is currently in /spec/lib/queries/otu/filter_spec.rb.
+ # Test coverage is currently in /spec/lib/queries/.
#
# !! When adding a new query tests do some linting of parameters, constants etc. Run them early and often !!
#
@@ -288,7 +288,6 @@ def initialize(query_params)
else
raise TaxonWorks::Error, "can not initialize filter with #{query_params.class.name}"
end
-
set_identifier_params(params)
set_nested_queries(params)
set_user_dates(params)
diff --git a/lib/tasks/development/data/geo/build_geographic_items_from_temporary_shape_tables.rake b/lib/tasks/development/data/geo/build_geographic_items_from_temporary_shape_tables.rake
index 1c87ff18ee..35a26c4918 100644
--- a/lib/tasks/development/data/geo/build_geographic_items_from_temporary_shape_tables.rake
+++ b/lib/tasks/development/data/geo/build_geographic_items_from_temporary_shape_tables.rake
@@ -16,7 +16,7 @@ namespace :tw do
def create_geographic_item_records
puts 'Creating GeographicItems'
geo_item = GeographicItem.new
- geo_item.multi_polygon = SIMPLE_SHAPES[:multi_polygon]
+ geo_item.geography = SIMPLE_SHAPES[:multi_polygon]
@dummy_multi_polygon = geo_item.geo_object
GeographicAreasGeographicItem.order('data_origin').find_each do |a|
create_geographic_item_and_update_related(a) if a.geographic_item_id.blank?
@@ -27,7 +27,7 @@ namespace :tw do
def create_geographic_item_and_update_related(a)
return unless a.geographic_item_id.blank? # allows the task to be stopped and started, be careful with this assumption!
- i = GeographicItem.create(multi_polygon: @dummy_multi_polygon)
+ i = GeographicItem.create(geography: @dummy_multi_polygon)
sql1 = "UPDATE geographic_areas_geographic_items SET geographic_item_id = '#{i.id}' where id = #{a.id};"
sql2 = "UPDATE geographic_items SET point = null, multi_polygon = ( select ST_Force3D(geom) from #{a.data_origin} where gid = '#{a.origin_gid}') WHERE id = #{i.id};"
ApplicationRecord.connection.execute(sql1)
@@ -65,7 +65,7 @@ namespace :tw do
expected_diff = '010700000000000000' # ?
IMPORT_TABLES.each_key do |t|
GeographicAreasGeographicItem.where(data_origin: t.to_s).limit(9).each do |i|
- if i.geographic_item.valid_geometry?
+ if i.geographic_item.st_is_valid
a = "SELECT St_AsBinary(geom) FROM #{i.data_origin} WHERE gid = #{i.origin_gid}"
b = "SELECT St_AsBinary(multi_polygon) FROM geographic_items WHERE id = #{i.geographic_item_id}"
sql1 = "SELECT St_SymDifference((#{a}), (#{b}));"
diff --git a/lib/tasks/development/data/geo/report_on_shapefiles.rake b/lib/tasks/development/data/geo/report_on_shapefiles.rake
index 372a39e6bb..27dd1d4324 100644
--- a/lib/tasks/development/data/geo/report_on_shapefiles.rake
+++ b/lib/tasks/development/data/geo/report_on_shapefiles.rake
@@ -1,23 +1,23 @@
namespace :tw do
namespace :development do
namespace :data do
- namespace :geo do
+ namespace :geo do
desc "Report on the shapefiles.\n
rake tw:development:data:geo:report_on_shapefiles data_directory=/Users/matt/src/sf/tw/gaz/ ?index=[something]"
task report_on_shapefiles: [:environment, :geo_dev_init, :data_directory] do
- BaseDir = @args[:data_directory]
+ BaseDir = @args[:data_directory]
Dir.glob(BaseDir + '**/*.shp').each { |filename|
# @mjy is unsure of use of index at this point
# index is only expected to work for the GADM V2 shape file set: intended *only* as a
# short-cut to a problem record.
# index = ENV['index']
- read_shape(filename, 0)
+ read_shape(filename, 0)
}
end
#noinspection RubyStringKeysInHashInspection
- # This does not update the database.
+ # This does not update the database.
# Should be considered early trial code?
def read_shape(filename, index)
# TODO: For some reason, Gis::FACTORY does not seem to be the default factory, so we are being specific here, to get the lenient polygon tests. This gets us past the problem polygons, but does not actually deal with the problem.
@@ -30,7 +30,7 @@ namespace :tw do
file.seek_index(index)
count = file.num_records
ess = (count == 1) ? '' : 's'
- puts filename
+ puts filename
puts "#{Time.now.strftime "%H:%M:%S"}: #{filename} contains #{count} item#{ess}."
# things to do before each file
@@ -117,7 +117,7 @@ namespace :tw do
record.geographic_area_type = area_type[0]
record.geographic_item = GeographicItem.new
- record.geographic_item.multi_polygon = item.geometry
+ record.geographic_item.geography = item.geometry
record.save!
diff --git a/lib/vendor/rgeo.rb b/lib/vendor/rgeo.rb
new file mode 100644
index 0000000000..3a2eb73241
--- /dev/null
+++ b/lib/vendor/rgeo.rb
@@ -0,0 +1,13 @@
+module Vendor
+ module Rgeo
+ def self.coord_sys_is_wgs84?(cs)
+ # TODO: what else could a valid cs.name for WGS 84 be?
+ wgs84_names = ['EPSG:4326', 'WGS 84', 'GCS_WGS_1984']
+
+ cs.geographic? &&
+ ((cs.name.present? && wgs84_names.include?(cs.name)) ||
+ (cs.authority_code.present? && cs.authority_code == '4326'))
+ end
+
+ end
+end
diff --git a/spec/config/initializers/gis_spec.rb b/spec/config/initializers/gis_spec.rb
new file mode 100644
index 0000000000..4020e75e32
--- /dev/null
+++ b/spec/config/initializers/gis_spec.rb
@@ -0,0 +1,77 @@
+require 'rails_helper'
+
+describe 'Gis', type: :model, group: [:geo, :shared_geo] do
+ # Spec'ing these since they're undocumented by rgeo and speak to our
+ # understanding of rgeo shape creation/interpretation and
+ # anti-meridian-crossing behavior (see anti_meridian_spec.rb for more on
+ # that).
+ context 'Gis::FACTORY' do
+ context 'latitudes' do
+ specify 'latitudes are clamped to pseudo-mercator range [-85.0511287, 85.0511287]' do
+ expect(Gis::FACTORY.point(0, 85.5).lat).to eq(85.0511287)
+ expect(Gis::FACTORY.point(0, -85.5).lat).to eq(-85.0511287)
+ end
+
+ specify 'valid shapes can become invalid in our factory representation due to latitude clamping' do
+ s = Gis::FACTORY.parse_wkt(
+ # This gets squished to four points with the same latitude
+ 'POLYGON ((0 85.5, 10 85.5, 15 85.7, 5 85.7, 0 85.5))'
+ )
+
+ expect(s.as_text).to eq(
+ 'POLYGON ((0.0 85.0511287 0.0, 10.0 85.0511287 0.0, 15.0 85.0511287 0.0, 5.0 85.0511287 0.0, 0.0 85.0511287 0.0))'
+ )
+
+ expect(s.valid?).to be false
+ end
+ end
+
+ context 'longitudes' do
+ context 'point coordinate longitudes are NEVER adjusted' do
+ specify 'point coordinate longitudes in [-180, 0] are NOT adjusted' do
+ expect(Gis::FACTORY.point(-170, 0).lon).to eq(-170)
+ expect(Gis::FACTORY.parse_wkt('POINT (-170 0)').lon).to eq(-170)
+ end
+
+ specify 'point coordinate longitudes in [180, 360] are NOT adjusted' do
+ expect(Gis::FACTORY.point(190, 0).lon).to eq(190)
+ expect(Gis::FACTORY.parse_wkt('POINT (190 0)').lon).to eq(190)
+ end
+
+ specify 'multipoint coordinate longitudes in [180, 360] are NOT adjusted' do
+ s = Gis::FACTORY.parse_wkt('MULTIPOINT ((190 0), (200 0))')
+ expect(s.first.lon).to eq(190)
+ end
+ end
+
+ context 'non-point shape coordinate longitudes ARE converted to the range [-180, 180], i.e. [180, 360] is converted to [-180, 0]' do
+ specify 'linestring' do
+ expect(
+ Gis::FACTORY.parse_wkt('LINESTRING (-170 0, 160 0, 190 10)').as_text
+ ).to eq('LINESTRING (-170.0 0.0 0.0, 160.0 0.0 0.0, -170.0 10.0 0.0)')
+ end
+
+ specify 'polygon' do
+ expect(Gis::FACTORY
+ .parse_wkt('POLYGON ((-170 0, 190 10, 175 0, -170 0))').as_text
+ ).to eq(
+ 'POLYGON ((-170.0 0.0 0.0, -170.0 10.0 0.0, 175.0 0.0 0.0, -170.0 0.0 0.0))'
+ )
+ end
+ end
+
+ specify '"Valid" shapes can become invalid due to longitude shifting/interpretation' do
+ # Lines between points are NOT always shortest-distance geodesics - see
+ # anti_meridian_spec.rb for further details.
+ s = Gis::FACTORY.parse_wkt(
+ 'POLYGON ((-200 -10, -200 10, -160 10, -190 0, -160 -10, -200 -10))'
+ )
+
+ # In this case the factory interpretation is that all lines between
+ # points in opposite hemispheres cross the meridian, not the
+ # anti-meridian, which causes this shape to have a self-intersection.
+ expect(s.valid?).to be false
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/controllers/geographic_items_controller_spec.rb b/spec/controllers/geographic_items_controller_spec.rb
index a0e2949289..5c216dc166 100644
--- a/spec/controllers/geographic_items_controller_spec.rb
+++ b/spec/controllers/geographic_items_controller_spec.rb
@@ -22,7 +22,7 @@
geographic_item = GeographicItem.create!(valid_attributes)
get :show, params: {id: geographic_item.to_param}, session: valid_session
- expect(assigns(:geographic_item)).to eq(geographic_item.becomes(GeographicItem::Point))
+ expect(assigns(:geographic_item)).to eq(geographic_item.becomes(GeographicItem))
end
end
@@ -30,7 +30,7 @@
it 'assigns the requested geographic_item as @geographic_item' do
geographic_item = GeographicItem.create!(valid_attributes)
get :edit, params: {id: geographic_item.to_param}, session: valid_session
- expect(assigns(:geographic_item)).to eq(geographic_item.becomes(GeographicItem::Point))
+ expect(assigns(:geographic_item)).to eq(geographic_item.becomes(GeographicItem))
end
end
diff --git a/spec/factories/gazetteer_imports.rb b/spec/factories/gazetteer_imports.rb
new file mode 100644
index 0000000000..89c1b49ae3
--- /dev/null
+++ b/spec/factories/gazetteer_imports.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :gazetteer_import, traits: [:housekeeping] do
+ factory :valid_gazetteer_import do
+ end
+ end
+end
diff --git a/spec/factories/gazetteers.rb b/spec/factories/gazetteers.rb
new file mode 100644
index 0000000000..f4424eb4b5
--- /dev/null
+++ b/spec/factories/gazetteers.rb
@@ -0,0 +1,30 @@
+FactoryBot.define do
+
+ factory :gazetteer, traits: [:housekeeping] do
+
+ factory :valid_gazetteer do
+ association :geographic_item, factory: :valid_geographic_item
+ name { 'gaz foo' }
+ end
+
+ factory :gazetteer_with_random_point do
+ association :geographic_item, factory: :random_point_geographic_item
+ name { 'gaz random point' }
+ end
+
+ factory :gazetteer_with_line_string do
+ association :geographic_item, factory: :geographic_item_with_line_string
+ name { 'gaz random line string' }
+ end
+
+ factory :gazetteer_with_polygon do
+ association :geographic_item, factory: :geographic_item_with_polygon
+ name { 'gaz random polygon' }
+ end
+
+ factory :gazetteer_with_multi_polygon do
+ association :geographic_item, factory: :geographic_item_with_multi_polygon
+ name { 'gaz random multi-polygon' }
+ end
+ end
+end
diff --git a/spec/factories/geographic_item/geometry_collection_factory.rb b/spec/factories/geographic_item/geometry_collection_factory.rb
deleted file mode 100644
index 2a2e918847..0000000000
--- a/spec/factories/geographic_item/geometry_collection_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_geometry_collection, class: 'GeographicItem::GeometryCollection' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/line_string_factory.rb b/spec/factories/geographic_item/line_string_factory.rb
deleted file mode 100644
index 4be93177d2..0000000000
--- a/spec/factories/geographic_item/line_string_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_line_string, class: 'GeographicItem::LineString' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/multi_line_string_factory.rb b/spec/factories/geographic_item/multi_line_string_factory.rb
deleted file mode 100644
index 3d8deba0de..0000000000
--- a/spec/factories/geographic_item/multi_line_string_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_multi_line_string, class: 'GeographicItem::MultiLineString' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/multi_point_factory.rb b/spec/factories/geographic_item/multi_point_factory.rb
deleted file mode 100644
index e10b81ff56..0000000000
--- a/spec/factories/geographic_item/multi_point_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_multi_point, class: 'GeographicItem::MultiPoint' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/multi_polygon_factory.rb b/spec/factories/geographic_item/multi_polygon_factory.rb
deleted file mode 100644
index baaf53dee0..0000000000
--- a/spec/factories/geographic_item/multi_polygon_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_multi_polygon, class: 'GeographicItem::MultiPolygon' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/point_factory.rb b/spec/factories/geographic_item/point_factory.rb
deleted file mode 100644
index 8bdae15b68..0000000000
--- a/spec/factories/geographic_item/point_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_point, class: 'GeographicItem::Point' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item/polygon_factory.rb b/spec/factories/geographic_item/polygon_factory.rb
deleted file mode 100644
index 7331ca4a41..0000000000
--- a/spec/factories/geographic_item/polygon_factory.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-FactoryBot.define do
- factory :geographic_item_polygon, class: 'GeographicItem::Polygon' do
-
- end
-
-end
diff --git a/spec/factories/geographic_item_factory.rb b/spec/factories/geographic_item_factory.rb
index afc45310dc..0890407bff 100644
--- a/spec/factories/geographic_item_factory.rb
+++ b/spec/factories/geographic_item_factory.rb
@@ -9,13 +9,13 @@
# FactoryBot.build(:geographic_item, :random_point)
trait :random_point do
- point { RSPEC_GEO_FACTORY.point(minLat + rng.rand * (maxLat - minLat),
+ geography { RSPEC_GEO_FACTORY.point(minLat + rng.rand * (maxLat - minLat),
minLng + rng.rand * (maxLng - minLng)) }
end
factory :geographic_item, traits: [:creator_and_updater] do
factory :valid_geographic_item, aliases: [:geographic_item_with_point_a] do
- point { GeoBuild::GI_POINT_A }
+ geography { GeoBuild::GI_POINT_A }
end
factory :random_point_geographic_item do
@@ -23,28 +23,27 @@
end
factory :geographic_item_with_point_m do
- point { GeoBuild::GI_POINT_M }
+ geography { GeoBuild::GI_POINT_M }
end
factory :geographic_item_with_point_u do
- point { GeoBuild::GI_POINT_U }
+ geography { GeoBuild::GI_POINT_U }
end
factory :geographic_item_with_point_c do
- point { GeoBuild::GI_POINT_C }
+ geography { GeoBuild::GI_POINT_C }
end
factory :geographic_item_with_line_string do
- line_string { GeoBuild::GI_LS01 }
+ geography { GeoBuild::GI_LS01 }
end
factory :geographic_item_with_polygon do
- polygon { GeoBuild::GI_POLYGON }
+ geography { GeoBuild::GI_POLYGON }
end
factory :geographic_item_with_multi_polygon do
- multi_polygon { GeoBuild::GI_MULTI_POLYGON }
- type { 'GeographicItem::MultiPolygon' }
+ geography { GeoBuild::GI_MULTI_POLYGON }
end
end
end
diff --git a/spec/factories/georeference/geo_locate_factory.rb b/spec/factories/georeference/geo_locate_factory.rb
index 1ad854c29f..7b53c320ec 100644
--- a/spec/factories/georeference/geo_locate_factory.rb
+++ b/spec/factories/georeference/geo_locate_factory.rb
@@ -10,7 +10,7 @@
# collecting_event { FactoryBot.build(:valid_collecting_event)}
association :geographic_item, factory: :geographic_item_with_point_c
error_radius { 7338.0 }
- error_geographic_item { GeographicItem.new(polygon: RSPEC_GEO_FACTORY.parse_wkt('POLYGON ((-88.2699379175 40.1379180207 0.0, -88.2694189175 40.1375100207 0.0, -88.2694509175 40.1374060207 0.0, -88.2690329175 40.1372730207 0.0, -88.2689819175 40.1373630207 0.0, -88.2689439175 40.1375880207 0.0, -88.2688339175 40.1401300207 0.0, -88.2691719175 40.1415490207 0.0, -88.2691719175 40.1416530207 0.0, -88.2691599175 40.1417120207 0.0, -88.2691429175 40.1419090207 0.0, -88.2695809175 40.1418370207 0.0, -88.2695859175 40.1420250207 0.0, -88.2693509175 40.1420950207 0.0, -88.2693799175 40.1421570207 0.0, -88.2692849175 40.1422310207 0.0, -88.2691779175 40.1431160207 0.0, -88.2696049175 40.1434840207 0.0, -88.2697659175 40.1435510207 0.0, -88.2702859175 40.1432970207 0.0, -88.2712409175 40.1436270207 0.0, -88.2711889175 40.1437380207 0.0, -88.2711549175 40.1438090207 0.0, -88.2716769175 40.1438240207 0.0, -88.2715969175 40.1439420207 0.0, -88.2719599175 40.1441460207 0.0, -88.2723289175 40.1443600207 0.0, -88.2724789175 40.1441270207 0.0, -88.2725889175 40.1441600207 0.0, -88.2725049175 40.1444260207 0.0, -88.2727769175 40.1444800207 0.0, -88.2727069175 40.1446540207 0.0, -88.2726349175 40.1450000207 0.0, -88.2725559175 40.1450000207 0.0, -88.2719339175 40.1447160207 0.0, -88.2715329175 40.1445010207 0.0, -88.2702929175 40.1441310207 0.0, -88.2703029175 40.1447020207 0.0, -88.2703059175 40.1448660207 0.0, -88.2709589175 40.1450450207 0.0, -88.2719749175 40.1453970207 0.0, -88.2720249175 40.1461770207 0.0, -88.2745079175 40.1460990207 0.0, -88.2747479175 40.1463620207 0.0, -88.2749159175 40.1464850207 0.0, -88.2750759175 40.1465150207 0.0, -88.2753009175 40.1465190207 0.0, -88.2752799175 40.1472240207 0.0, -88.2769659175 40.1473490207 0.0, -88.2769669175 40.1476430207 0.0, -88.2752829175 40.1476010207 0.0, -88.2749959175 40.1487340207 0.0, -88.2758409175 40.1487230207 0.0, -88.2764749175 40.1487280207 0.0, -88.2769719175 40.1487050207 0.0, -88.2808679176 40.1481790207 0.0, -88.2814679176 40.1477910207 0.0, -88.2818739176 40.1475280207 0.0, -88.2822109176 40.1468660207 0.0, -88.2824339176 40.1462690207 0.0, -88.2823879176 40.1455250207 0.0, -88.2823329176 40.1451960207 0.0, -88.2823939176 40.1450440207 0.0, -88.2824309176 40.1449460207 0.0, -88.2824749176 40.1448500207 0.0, -88.2827829176 40.1444400207 0.0, -88.2830389176 40.1444320207 0.0, -88.2832379176 40.1444260207 0.0, -88.2842419176 40.1443110207 0.0, -88.2848379176 40.1442640207 0.0, -88.2854609176 40.1441730207 0.0, -88.2858139176 40.1440440207 0.0, -88.2862149176 40.1437670207 0.0, -88.2864479176 40.1436000207 0.0, -88.2867319176 40.1434570207 0.0, -88.2869249176 40.1434520207 0.0, -88.2870509176 40.1435230207 0.0, -88.2871169176 40.1435490207 0.0, -88.2882049176 40.1439130207 0.0, -88.2877929176 40.1448710207 0.0, -88.2873729176 40.1472920207 0.0, -88.2875759176 40.1478710207 0.0, -88.2935339176 40.1499700207 0.0, -88.2951569176 40.1507950207 0.0, -88.2959119176 40.1511640207 0.0, -88.2959339176 40.1517930207 0.0, -88.2959339176 40.1524080207 0.0, -88.2959759176 40.1527150207 0.0, -88.2960249176 40.1528760207 0.0, -88.2961019176 40.1530510207 0.0, -88.2962069176 40.1532250207 0.0, -88.2963419176 40.1534130207 0.0, -88.2964829176 40.1535680207 0.0, -88.2967669176 40.1538350207 0.0, -88.2969229176 40.1539610207 0.0, -88.2980319176 40.1549000207 0.0, -88.2983929176 40.1552160207 0.0, -88.2985249176 40.1553680207 0.0, -88.2986509176 40.1555360207 0.0, -88.2987639176 40.1557180207 0.0, -88.2988309176 40.1558470207 0.0, -88.2989079176 40.1560360207 0.0, -88.2989679176 40.1562850207 0.0, -88.2989699176 40.1567430207 0.0, -88.2989309176 40.1569900207 0.0, -88.2988409176 40.1572420207 0.0, -88.2987769176 40.1573600207 0.0, -88.2986679176 40.1575260207 0.0, -88.2985249176 40.1577090207 0.0, -88.2983469176 40.1579130207 0.0, -88.2981109176 40.1581830207 0.0, -88.2980459176 40.1582610207 0.0, -88.2979819176 40.1583370207 0.0, -88.2961539176 40.1567660207 0.0, -88.2937499176 40.1547220207 0.0, -88.2931789176 40.1542330207 0.0, -88.2876939176 40.1495810207 0.0, -88.2876389176 40.1497300207 0.0, -88.2875779176 40.1499350207 0.0, -88.2830889176 40.1496570207 0.0, -88.2813489176 40.1494580207 0.0, -88.2802749175 40.1494620207 0.0, -88.2800369175 40.1494750207 0.0, -88.2797929175 40.1494780207 0.0, -88.2769839175 40.1495000207 0.0, -88.2757809175 40.1495950207 0.0, -88.2759029175 40.1504800207 0.0, -88.2769959175 40.1504800207 0.0, -88.2770079175 40.1516580207 0.0, -88.2770129175 40.1519290207 0.0, -88.2770149175 40.1520720207 0.0, -88.2770189175 40.1523070207 0.0, -88.2762159175 40.1530050207 0.0, -88.2745889175 40.1544400207 0.0, -88.2746399175 40.1546200207 0.0, -88.2748599175 40.1563480207 0.0, -88.2749489175 40.1576120207 0.0, -88.2749549175 40.1576710207 0.0, -88.2749519175 40.1577340207 0.0, -88.2749459175 40.1579400207 0.0, -88.2758939175 40.1578840207 0.0, -88.2770359175 40.1578730207 0.0, -88.2770399175 40.1577900207 0.0, -88.2770439175 40.1577240207 0.0, -88.2770449175 40.1576500207 0.0, -88.2770609175 40.1563080207 0.0, -88.2770729175 40.1559180207 0.0, -88.2778969175 40.1556830207 0.0, -88.2789139175 40.1555090207 0.0, -88.2806589176 40.1555130207 0.0, -88.2844099176 40.1578440207 0.0, -88.2832309176 40.1585310207 0.0, -88.2831939176 40.1624750208 0.0, -88.2779909175 40.1624900208 0.0, -88.2779959175 40.1616910208 0.0, -88.2771059175 40.1617370208 0.0, -88.2769059175 40.1617340208 0.0, -88.2768959175 40.1593910207 0.0, -88.2694489175 40.1590980207 0.0, -88.2692729175 40.1590910207 0.0, -88.2692909175 40.1588480207 0.0, -88.2693799175 40.1586100207 0.0, -88.2694969175 40.1583720207 0.0, -88.2697069175 40.1581600207 0.0, -88.2696089175 40.1581600207 0.0, -88.2692929175 40.1581590207 0.0, -88.2670149175 40.1575690207 0.0, -88.2667059175 40.1574360207 0.0, -88.2657079175 40.1570060207 0.0, -88.2647769175 40.1566580207 0.0, -88.2647769175 40.1565960207 0.0, -88.2647829175 40.1564650207 0.0, -88.2664309175 40.1556340207 0.0, -88.2658499175 40.1556350207 0.0, -88.2658569175 40.1539210207 0.0, -88.2664319175 40.1539200207 0.0, -88.2664329175 40.1525100207 0.0, -88.2665769175 40.1525140207 0.0, -88.2691989175 40.1525300207 0.0, -88.2691929175 40.1520630207 0.0, -88.2691889175 40.1517860207 0.0, -88.2666259175 40.1517110207 0.0, -88.2664969175 40.1517090207 0.0, -88.2657859175 40.1517370207 0.0, -88.2657179175 40.1509970207 0.0, -88.2665079175 40.1510350207 0.0, -88.2664769175 40.1500330207 0.0, -88.2664669175 40.1498330207 0.0, -88.2664509175 40.1484940207 0.0, -88.2664209175 40.1469820207 0.0, -88.2664229175 40.1467290207 0.0, -88.2664219175 40.1461990207 0.0, -88.2660819175 40.1462060207 0.0, -88.2657829175 40.1462120207 0.0, -88.2645659175 40.1464580207 0.0, -88.2619779175 40.1469820207 0.0, -88.2619819175 40.1473840207 0.0, -88.2579499175 40.1469560207 0.0, -88.2579399175 40.1465160207 0.0, -88.2579189175 40.1452580207 0.0, -88.2575909175 40.1451250207 0.0, -88.2564839175 40.1451320207 0.0, -88.2488059175 40.1451920207 0.0, -88.2485279175 40.1451930207 0.0, -88.2485169175 40.1456130207 0.0, -88.2485279175 40.1460380207 0.0, -88.2482809175 40.1460320207 0.0, -88.2482809175 40.1462550207 0.0, -88.2480509175 40.1462550207 0.0, -88.2480509175 40.1468130207 0.0, -88.2478519175 40.1468130207 0.0, -88.2467419175 40.1468130207 0.0, -88.2465069175 40.1468130207 0.0, -88.2465069175 40.1460320207 0.0, -88.2456309175 40.1460380207 0.0, -88.2456289175 40.1456130207 0.0, -88.2455699175 40.1443560207 0.0, -88.2454669175 40.1444120207 0.0, -88.2453489175 40.1444350207 0.0, -88.2452319175 40.1444460207 0.0, -88.2441499175 40.1444410207 0.0, -88.2408969175 40.1444130207 0.0, -88.2407789175 40.1444020207 0.0, -88.2406979175 40.1443960207 0.0, -88.2405949175 40.1443740207 0.0, -88.2390569175 40.1444070207 0.0, -88.2387699175 40.1444300207 0.0, -88.2389189175 40.1493700207 0.0, -88.2391769175 40.1563040207 0.0, -88.2392089175 40.1600920207 0.0, -88.2318049175 40.1600520207 0.0, -88.2272139175 40.1599860207 0.0, -88.2271789175 40.1607360207 0.0, -88.2271999175 40.1641090208 0.0, -88.2244449175 40.1641480208 0.0, -88.2208259175 40.1641200208 0.0, -88.2222659175 40.1606850207 0.0, -88.2241799175 40.1563300207 0.0, -88.2260719175 40.1520830207 0.0, -88.2275839175 40.1485910207 0.0, -88.2290229175 40.1450520207 0.0, -88.2326749175 40.1449470207 0.0, -88.2326439175 40.1438400207 0.0, -88.2357619175 40.1437390207 0.0, -88.2378949175 40.1437310207 0.0, -88.2390439175 40.1436920207 0.0, -88.2390419175 40.1435950207 0.0, -88.2390229175 40.1426800207 0.0, -88.2390129175 40.1417560207 0.0, -88.2378879175 40.1417440207 0.0, -88.2358699175 40.1417590207 0.0, -88.2358939175 40.1411200207 0.0, -88.2361929175 40.1411060207 0.0, -88.2364019175 40.1396900207 0.0, -88.2362729175 40.1396440207 0.0, -88.2358119175 40.1379660207 0.0, -88.2358259175 40.1351040207 0.0, -88.2330329175 40.1351040207 0.0, -88.2327739175 40.1351080207 0.0, -88.2326309175 40.1351070207 0.0, -88.2325349175 40.1351070207 0.0, -88.2325989175 40.1349510207 0.0, -88.2326259175 40.1348880207 0.0, -88.2326679175 40.1347920207 0.0, -88.2327539175 40.1347310207 0.0, -88.2328059175 40.1345940207 0.0, -88.2335779175 40.1328740207 0.0, -88.2338679175 40.1324320207 0.0, -88.2341199175 40.1317740207 0.0, -88.2342589175 40.1317990207 0.0, -88.2344729175 40.1317910207 0.0, -88.2356249175 40.1318060207 0.0, -88.2356379175 40.1309200207 0.0, -88.2346849175 40.1304570207 0.0, -88.2341559175 40.1309960207 0.0, -88.2340829175 40.1309900207 0.0, -88.2339849175 40.1309900207 0.0, -88.2335879175 40.1309900207 0.0, -88.2299539175 40.1310020207 0.0, -88.2290679175 40.1309360207 0.0, -88.2290699175 40.1305940207 0.0, -88.2290699175 40.1293820207 0.0, -88.2281079175 40.1293810207 0.0, -88.2281079175 40.1290110207 0.0, -88.2267949175 40.1289690207 0.0, -88.2262929175 40.1289730207 0.0, -88.2262929175 40.1286210207 0.0, -88.2256409175 40.1286220207 0.0, -88.2252769175 40.1286210207 0.0, -88.2259989175 40.1277740207 0.0, -88.2259809175 40.1274940207 0.0, -88.2259709175 40.1273620207 0.0, -88.2267119175 40.1273540207 0.0, -88.2276389175 40.1273430207 0.0, -88.2284139175 40.1273430207 0.0, -88.2285949175 40.1273420207 0.0, -88.2288739175 40.1273390207 0.0, -88.2288719175 40.1262580207 0.0, -88.2288989175 40.1260710207 0.0, -88.2289069175 40.1258470207 0.0, -88.2280059175 40.1258480207 0.0, -88.2279919175 40.1250810207 0.0, -88.2279839175 40.1246830207 0.0, -88.2288669175 40.1246850207 0.0, -88.2289819175 40.1246900207 0.0, -88.2289699175 40.1238710207 0.0, -88.2289639175 40.1236620207 0.0, -88.2288569175 40.1238470207 0.0, -88.2288509175 40.1231300207 0.0, -88.2288459175 40.1226230207 0.0, -88.2288369175 40.1221790207 0.0, -88.2288319175 40.1212280207 0.0, -88.2288349175 40.1207790207 0.0, -88.2288219175 40.1202710207 0.0, -88.2288009175 40.1193220207 0.0, -88.2287969175 40.1191140207 0.0, -88.2288029175 40.1189820207 0.0, -88.2287979175 40.1189340207 0.0, -88.2287969175 40.1188370207 0.0, -88.2287939175 40.1183480207 0.0, -88.2287849175 40.1173860207 0.0, -88.2287709175 40.1163870207 0.0, -88.2288599175 40.1163890207 0.0, -88.2288259175 40.1135150207 0.0, -88.2288169175 40.1127440207 0.0, -88.2288119175 40.1122470207 0.0, -88.2287969175 40.1111530207 0.0, -88.2288239175 40.1103320207 0.0, -88.2287579175 40.1091430207 0.0, -88.2287269175 40.1059510207 0.0, -88.2287249175 40.1054870207 0.0, -88.2289289175 40.1054880207 0.0, -88.2288799175 40.1041940207 0.0, -88.2288539175 40.1030760207 0.0, -88.2288539175 40.1022540207 0.0, -88.2288339175 40.1014970207 0.0, -88.2287329175 40.1014980207 0.0, -88.2286329175 40.1014980207 0.0, -88.2287219175 40.1014850207 0.0, -88.2288519175 40.1014780207 0.0, -88.2288369175 40.1005780207 0.0, -88.2301899175 40.1005660207 0.0, -88.2310459175 40.1005600207 0.0, -88.2325089175 40.1005560207 0.0, -88.2333939175 40.1005460207 0.0, -88.2333699175 40.0990220207 0.0, -88.2333399175 40.0981690207 0.0, -88.2333359175 40.0980650207 0.0, -88.2328219175 40.0980480207 0.0, -88.2310829175 40.0980610207 0.0, -88.2286919175 40.0980650207 0.0, -88.2286569175 40.0959180207 0.0, -88.2285969175 40.0955070207 0.0, -88.2311059175 40.0954430207 0.0, -88.2312349175 40.0954860207 0.0, -88.2332969175 40.0955490207 0.0, -88.2347279175 40.0954610207 0.0, -88.2357119175 40.0953780207 0.0, -88.2362889175 40.0953390207 0.0, -88.2363399175 40.0953160207 0.0, -88.2385729175 40.0952720207 0.0, -88.2414529175 40.0952030207 0.0, -88.2443629175 40.0952050207 0.0, -88.2445099175 40.0944610207 0.0, -88.2447989175 40.0914670207 0.0, -88.2448509175 40.0909330207 0.0, -88.2446249175 40.0909290207 0.0, -88.2446399175 40.0907560207 0.0, -88.2438699175 40.0907430207 0.0, -88.2438389175 40.0908980207 0.0, -88.2436379175 40.0908990207 0.0, -88.2435849175 40.0912980207 0.0, -88.2435939175 40.0914820207 0.0, -88.2433909175 40.0914770207 0.0, -88.2432299175 40.0914750207 0.0, -88.2432179175 40.0909720207 0.0, -88.2431949175 40.0908700207 0.0, -88.2431499175 40.0908380207 0.0, -88.2430579175 40.0908220207 0.0, -88.2430379175 40.0908220207 0.0, -88.2429919175 40.0874370207 0.0, -88.2448279175 40.0870540207 0.0, -88.2452799175 40.0870570207 0.0, -88.2455489175 40.0870620207 0.0, -88.2457899175 40.0852100207 0.0, -88.2459049175 40.0852570207 0.0, -88.2460559175 40.0841200207 0.0, -88.2460759175 40.0840090207 0.0, -88.2461039175 40.0838200207 0.0, -88.2461319175 40.0835820207 0.0, -88.2461429175 40.0835100207 0.0, -88.2464089175 40.0835120207 0.0, -88.2465539175 40.0835100207 0.0, -88.2468669175 40.0835090207 0.0, -88.2484209175 40.0835060207 0.0, -88.2490619175 40.0834990207 0.0, -88.2493419175 40.0834840207 0.0, -88.2494179175 40.0835610207 0.0, -88.2496439175 40.0835600207 0.0, -88.2506609175 40.0835640207 0.0, -88.2511039175 40.0835630207 0.0, -88.2516819175 40.0835750207 0.0, -88.2527409175 40.0835860207 0.0, -88.2537559175 40.0835860207 0.0, -88.2547969175 40.0835940207 0.0, -88.2574609175 40.0836170207 0.0, -88.2574479175 40.0826020207 0.0, -88.2571489175 40.0826190207 0.0, -88.2571319175 40.0822840207 0.0, -88.2574439175 40.0822820207 0.0, -88.2574339175 40.0814870207 0.0, -88.2574209175 40.0806940207 0.0, -88.2574219175 40.0803910207 0.0, -88.2574219175 40.0802720207 0.0, -88.2574159175 40.0792300207 0.0, -88.2574169175 40.0791180207 0.0, -88.2574069175 40.0780780207 0.0, -88.2574039175 40.0775480207 0.0, -88.2573989175 40.0771140207 0.0, -88.2573949175 40.0768220207 0.0, -88.2577309175 40.0766840207 0.0, -88.2614989175 40.0767370207 0.0, -88.2619489175 40.0765900207 0.0, -88.2707989175 40.0764250207 0.0, -88.2715599175 40.0768370207 0.0, -88.2715239175 40.0836710207 0.0, -88.2708329175 40.0836600207 0.0, -88.2706829175 40.0836610207 0.0, -88.2706089175 40.0836690207 0.0, -88.2698599175 40.0836640207 0.0, -88.2686649175 40.0836570207 0.0, -88.2674419175 40.0836550207 0.0, -88.2670449175 40.0836540207 0.0, -88.2669639175 40.0850200207 0.0, -88.2673559175 40.0850510207 0.0, -88.2674739175 40.0842260207 0.0, -88.2678509175 40.0842190207 0.0, -88.2677159175 40.0865060207 0.0, -88.2677739175 40.0872270207 0.0, -88.2694399175 40.0872250207 0.0, -88.2699469175 40.0872430207 0.0, -88.2701829175 40.0873020207 0.0, -88.2700759175 40.0868960207 0.0, -88.2705019175 40.0868950207 0.0, -88.2704909175 40.0873010207 0.0, -88.2720569175 40.0871960207 0.0, -88.2753329175 40.0872240207 0.0, -88.2763729175 40.0873340207 0.0, -88.2763719175 40.0872410207 0.0, -88.2759969175 40.0870250207 0.0, -88.2756689175 40.0868530207 0.0, -88.2754079175 40.0869000207 0.0, -88.2755299175 40.0865950207 0.0, -88.2756119175 40.0865940207 0.0, -88.2758459175 40.0866120207 0.0, -88.2763699175 40.0870460207 0.0, -88.2763689175 40.0869230207 0.0, -88.2763599175 40.0863190207 0.0, -88.2763469175 40.0855380207 0.0, -88.2763199175 40.0837030207 0.0, -88.2763149175 40.0834320207 0.0, -88.2763049175 40.0825170207 0.0, -88.2773889175 40.0826780207 0.0, -88.2774189175 40.0834280207 0.0, -88.2790239175 40.0834610207 0.0, -88.2796329175 40.0832160207 0.0, -88.2796279175 40.0824130207 0.0, -88.2790189175 40.0824070207 0.0, -88.2789969175 40.0820510207 0.0, -88.2796269175 40.0820760207 0.0, -88.2796269175 40.0819610207 0.0, -88.2795989175 40.0817230207 0.0, -88.2795349175 40.0815780207 0.0, -88.2794339175 40.0814360207 0.0, -88.2793659175 40.0813720207 0.0, -88.2792849175 40.0813100207 0.0, -88.2791449175 40.0812470207 0.0, -88.2788889175 40.0811780207 0.0, -88.2787939175 40.0811660207 0.0, -88.2769879175 40.0811460207 0.0, -88.2763829175 40.0811170207 0.0, -88.2762909175 40.0811140207 0.0, -88.2762869175 40.0804170207 0.0, -88.2762829175 40.0800160207 0.0, -88.2762749175 40.0791340207 0.0, -88.2761879175 40.0740310207 0.0, -88.2811179176 40.0740120207 0.0, -88.2811259176 40.0734400207 0.0, -88.2810779176 40.0733920207 0.0, -88.2810409176 40.0733330207 0.0, -88.2809999176 40.0730960207 0.0, -88.2809869176 40.0728640207 0.0, -88.2810029176 40.0723840207 0.0, -88.2809729176 40.0709380207 0.0, -88.2809549176 40.0699920207 0.0, -88.2809569176 40.0692390207 0.0, -88.2832339176 40.0692580207 0.0, -88.2883059176 40.0693170207 0.0, -88.2883429176 40.0707940207 0.0, -88.2889439176 40.0748830207 0.0, -88.2891619176 40.0758480207 0.0, -88.2890199176 40.0765390207 0.0, -88.2864479176 40.0768720207 0.0, -88.2851129176 40.0763130207 0.0, -88.2833079176 40.0757600207 0.0, -88.2833039176 40.0761750207 0.0, -88.2806479176 40.0761960207 0.0, -88.2807249176 40.0793360207 0.0, -88.2808479176 40.0802050207 0.0, -88.2808089176 40.0804980207 0.0, -88.2807029176 40.0835500207 0.0, -88.2811879176 40.0834920207 0.0, -88.2861959176 40.0835110207 0.0, -88.2864749176 40.0834730207 0.0, -88.2864739176 40.0836800207 0.0, -88.2872489176 40.0836740207 0.0, -88.2872629176 40.0838470207 0.0, -88.2877869176 40.0838530207 0.0, -88.2900159176 40.0838710207 0.0, -88.2924959176 40.0838940207 0.0, -88.2934389176 40.0839040207 0.0, -88.2937569176 40.0839100207 0.0, -88.2949579176 40.0839300207 0.0, -88.2950099176 40.0815760207 0.0, -88.2950009176 40.0812920207 0.0, -88.2945229176 40.0812970207 0.0, -88.2945719176 40.0811280207 0.0, -88.2945549176 40.0808880207 0.0, -88.2942109176 40.0808580207 0.0, -88.2934689176 40.0807720207 0.0, -88.2930259176 40.0807400207 0.0, -88.2927119176 40.0799070207 0.0, -88.2932579176 40.0788420207 0.0, -88.2930859176 40.0775430207 0.0, -88.2920859176 40.0777210207 0.0, -88.2921809176 40.0807530207 0.0, -88.2915949176 40.0807280207 0.0, -88.2911109176 40.0807490207 0.0, -88.2912309176 40.0796550207 0.0, -88.2905429176 40.0797080207 0.0, -88.2905399176 40.0794290207 0.0, -88.2910349176 40.0793940207 0.0, -88.2909229176 40.0791250207 0.0, -88.2910899176 40.0790960207 0.0, -88.2911999176 40.0776420207 0.0, -88.2916249176 40.0777320207 0.0, -88.2916159176 40.0774940207 0.0, -88.2914409176 40.0775040207 0.0, -88.2912349176 40.0774960207 0.0, -88.2912199176 40.0772390207 0.0, -88.2914359176 40.0772770207 0.0, -88.2918549176 40.0771280207 0.0, -88.2924579176 40.0768630207 0.0, -88.2931839176 40.0768260207 0.0, -88.2931629176 40.0770640207 0.0, -88.2931709176 40.0771770207 0.0, -88.2938129176 40.0771740207 0.0, -88.2938249176 40.0772890207 0.0, -88.2939089176 40.0773730207 0.0, -88.2940999176 40.0773700207 0.0, -88.2941249176 40.0773270207 0.0, -88.2941359176 40.0772560207 0.0, -88.2941299176 40.0771730207 0.0, -88.2949449176 40.0771780207 0.0, -88.2949379176 40.0758310207 0.0, -88.2951109176 40.0758290207 0.0, -88.3037509176 40.0758370207 0.0, -88.3037569176 40.0764130207 0.0, -88.3038759176 40.0764150207 0.0, -88.3039719176 40.0764120207 0.0, -88.3040979176 40.0764140207 0.0, -88.3041179176 40.0776810207 0.0, -88.3041569176 40.0783820207 0.0, -88.3042289176 40.0792890207 0.0, -88.3043149176 40.0801260207 0.0, -88.3043919176 40.0807620207 0.0, -88.3137929176 40.0808050207 0.0, -88.3140469176 40.0808320207 0.0, -88.3141029176 40.0841320207 0.0, -88.3124959176 40.0841250207 0.0, -88.3108159176 40.0841070207 0.0, -88.3080419176 40.0840740207 0.0, -88.3071149176 40.0840540207 0.0, -88.3050069176 40.0840260207 0.0, -88.3047409176 40.0840250207 0.0, -88.3046679176 40.0840240207 0.0, -88.3045639176 40.0840240207 0.0, -88.3044009176 40.0840230207 0.0, -88.3041979176 40.0840220207 0.0, -88.3016319176 40.0839970207 0.0, -88.3005509176 40.0839900207 0.0, -88.3004489176 40.0839890207 0.0, -88.2987589176 40.0839730207 0.0, -88.2987639176 40.0841020207 0.0, -88.2980169176 40.0849980207 0.0, -88.2978809176 40.0852280207 0.0, -88.2977539176 40.0854380207 0.0, -88.2975069176 40.0854640207 0.0, -88.2974039176 40.0854580207 0.0, -88.2964029176 40.0854640207 0.0, -88.2951779176 40.0853020207 0.0, -88.2951849176 40.0863370207 0.0, -88.2951889176 40.0866230207 0.0, -88.2951939176 40.0878100207 0.0, -88.2951949176 40.0886710207 0.0, -88.2952009176 40.0897370207 0.0, -88.2952109176 40.0908150207 0.0, -88.2952139176 40.0911630207 0.0, -88.2952259176 40.0933020207 0.0, -88.2952289176 40.0951230207 0.0, -88.2952259176 40.0960230207 0.0, -88.2952339176 40.0976360207 0.0, -88.2952419176 40.0984640207 0.0, -88.2952449176 40.0992210207 0.0, -88.2952449176 40.0999440207 0.0, -88.2952559176 40.1012930207 0.0, -88.2946119176 40.1012930207 0.0, -88.2946289176 40.1015740207 0.0, -88.2952579176 40.1015690207 0.0, -88.2952629176 40.1020760207 0.0, -88.2952729176 40.1024860207 0.0, -88.2952789176 40.1027510207 0.0, -88.2952829176 40.1035260207 0.0, -88.2953169176 40.1056980207 0.0, -88.2970009176 40.1057200207 0.0, -88.2986299176 40.1057860207 0.0, -88.3021129176 40.1058270207 0.0, -88.3022179176 40.1057470207 0.0, -88.3040989176 40.1056900207 0.0, -88.3040869176 40.1061910207 0.0, -88.3047309176 40.1059720207 0.0, -88.3048539176 40.1059780207 0.0, -88.3048439176 40.1063560207 0.0, -88.3049229176 40.1063550207 0.0, -88.3049209176 40.1055820207 0.0, -88.3049089176 40.1027780207 0.0, -88.3050309176 40.1027750207 0.0, -88.3059199176 40.1032180207 0.0, -88.3060289176 40.1032480207 0.0, -88.3093139176 40.1036720207 0.0, -88.3103869176 40.1037630207 0.0, -88.3103899176 40.1037250207 0.0, -88.3104459176 40.1035060207 0.0, -88.3105409176 40.1033270207 0.0, -88.3107279176 40.1031130207 0.0, -88.3109389176 40.1029720207 0.0, -88.3111269176 40.1028760207 0.0, -88.3113269176 40.1028040207 0.0, -88.3115879176 40.1027560207 0.0, -88.3119299176 40.1027430207 0.0, -88.3123049176 40.1027550207 0.0, -88.3125959176 40.1028050207 0.0, -88.3127629176 40.1028660207 0.0, -88.3129009176 40.1029490207 0.0, -88.3130689176 40.1030790207 0.0, -88.3131209176 40.1031290207 0.0, -88.3132189176 40.1032320207 0.0, -88.3143519176 40.1027170207 0.0, -88.3143639176 40.1035540207 0.0, -88.3144049176 40.1058550207 0.0, -88.3144219176 40.1065620207 0.0, -88.3144389176 40.1077600207 0.0, -88.3144639176 40.1088000207 0.0, -88.3144709176 40.1091040207 0.0, -88.3144769176 40.1094420207 0.0, -88.3172839176 40.1092760207 0.0, -88.3172929176 40.1089890207 0.0, -88.3173259176 40.1058900207 0.0, -88.3180159176 40.1058690207 0.0, -88.3215459176 40.1058690207 0.0, -88.3215489176 40.1124680207 0.0, -88.3215439176 40.1126830207 0.0, -88.3145259176 40.1128000207 0.0, -88.3141369176 40.1128220207 0.0, -88.3141709176 40.1111100207 0.0, -88.3141889176 40.1104590207 0.0, -88.3141759176 40.1101010207 0.0, -88.3127929176 40.1100220207 0.0, -88.3117729176 40.1099990207 0.0, -88.3115649176 40.1088250207 0.0, -88.3111399176 40.1087850207 0.0, -88.3112019176 40.1082790207 0.0, -88.3063529176 40.1083230207 0.0, -88.3058249176 40.1083240207 0.0, -88.3058309176 40.1087750207 0.0, -88.3058459176 40.1100150207 0.0, -88.3057469176 40.1100120207 0.0, -88.3051759176 40.1099940207 0.0, -88.3051109176 40.1099930207 0.0, -88.3051659176 40.1124020207 0.0, -88.3051639176 40.1126760207 0.0, -88.3049909176 40.1126710207 0.0, -88.3048239176 40.1126710207 0.0, -88.3044389176 40.1126730207 0.0, -88.3034609176 40.1126980207 0.0, -88.2978629176 40.1126260207 0.0, -88.2963299176 40.1126490207 0.0, -88.2954549176 40.1126340207 0.0, -88.2954549176 40.1127350207 0.0, -88.2941329176 40.1129920207 0.0, -88.2941169176 40.1130370207 0.0, -88.2939439176 40.1130300207 0.0, -88.2938339176 40.1130550207 0.0, -88.2928849176 40.1132110207 0.0, -88.2925109176 40.1134080207 0.0, -88.2925619176 40.1135620207 0.0, -88.2928599176 40.1134400207 0.0, -88.2932559176 40.1133030207 0.0, -88.2930019176 40.1135000207 0.0, -88.2926379176 40.1137090207 0.0, -88.2918009176 40.1148490207 0.0, -88.2918689176 40.1148270207 0.0, -88.2919799176 40.1148060207 0.0, -88.2923059176 40.1147710207 0.0, -88.2927259176 40.1147730207 0.0, -88.2943119176 40.1147660207 0.0, -88.2945359176 40.1147810207 0.0, -88.2947229176 40.1148240207 0.0, -88.2948859176 40.1148840207 0.0, -88.2950179176 40.1149220207 0.0, -88.2951359176 40.1149370207 0.0, -88.2954739176 40.1149490207 0.0, -88.2955069176 40.1175490207 0.0, -88.2955509176 40.1195370207 0.0, -88.2955539176 40.1197840207 0.0, -88.2955559176 40.1200370207 0.0, -88.2973569176 40.1200550207 0.0, -88.2985229176 40.1200660207 0.0, -88.2985139176 40.1198080207 0.0, -88.2985169176 40.1196960207 0.0, -88.2985349176 40.1194240207 0.0, -88.2984369176 40.1182020207 0.0, -88.2985869176 40.1179400207 0.0, -88.2983599176 40.1176520207 0.0, -88.2985809176 40.1176490207 0.0, -88.2985779176 40.1175450207 0.0, -88.2990509176 40.1175590207 0.0, -88.2990159176 40.1176740207 0.0, -88.3002049176 40.1175390207 0.0, -88.3002199176 40.1182600207 0.0, -88.3002449176 40.1196190207 0.0, -88.3003229176 40.1197840207 0.0, -88.3000039176 40.1197990207 0.0, -88.2996099176 40.1198140207 0.0, -88.2996449176 40.1200750207 0.0, -88.3008319176 40.1200880207 0.0, -88.3006279176 40.1198330207 0.0, -88.3008669176 40.1197590207 0.0, -88.3013149176 40.1197200207 0.0, -88.3014889176 40.1196800207 0.0, -88.3017179176 40.1196090207 0.0, -88.3019389176 40.1195110207 0.0, -88.3020929176 40.1194200207 0.0, -88.3023179176 40.1192110207 0.0, -88.3026989176 40.1187370207 0.0, -88.3028339176 40.1185990207 0.0, -88.3029919176 40.1184770207 0.0, -88.3039059176 40.1180900207 0.0, -88.3040439176 40.1180160207 0.0, -88.3042179176 40.1178890207 0.0, -88.3044189176 40.1176600207 0.0, -88.3045179176 40.1175260207 0.0, -88.3045889176 40.1173920207 0.0, -88.3046499176 40.1172340207 0.0, -88.3046989176 40.1169580207 0.0, -88.3048429176 40.1155740207 0.0, -88.3050059176 40.1154770207 0.0, -88.3051699176 40.1155350207 0.0, -88.3053009176 40.1171280207 0.0, -88.3053309176 40.1172280207 0.0, -88.3053479176 40.1172850207 0.0, -88.3054309176 40.1174630207 0.0, -88.3055399176 40.1176170207 0.0, -88.3056939176 40.1177780207 0.0, -88.3058319176 40.1178890207 0.0, -88.3060489176 40.1180230207 0.0, -88.3067439176 40.1183430207 0.0, -88.3069019176 40.1184410207 0.0, -88.3070229176 40.1185400207 0.0, -88.3074209176 40.1190610207 0.0, -88.3075989176 40.1192740207 0.0, -88.3077139176 40.1193960207 0.0, -88.3078719176 40.1195100207 0.0, -88.3080729176 40.1196130207 0.0, -88.3082609176 40.1196880207 0.0, -88.3083989176 40.1197270207 0.0, -88.3087619176 40.1197980207 0.0, -88.3102029176 40.1199330207 0.0, -88.3104399176 40.1200800207 0.0, -88.3101859176 40.1202000207 0.0, -88.3092619176 40.1202600207 0.0, -88.3087229176 40.1203270207 0.0, -88.3085169176 40.1203790207 0.0, -88.3084079176 40.1204220207 0.0, -88.3083279176 40.1204540207 0.0, -88.3081459176 40.1205520207 0.0, -88.3079489176 40.1206780207 0.0, -88.3077829176 40.1208280207 0.0, -88.3073819176 40.1212900207 0.0, -88.3072519176 40.1214160207 0.0, -88.3071179176 40.1215190207 0.0, -88.3069589176 40.1216130207 0.0, -88.3062069176 40.1219570207 0.0, -88.3059699176 40.1220830207 0.0, -88.3058639176 40.1221580207 0.0, -88.3057209176 40.1222880207 0.0, -88.3056329176 40.1224020207 0.0, -88.3055539176 40.1225330207 0.0, -88.3054549176 40.1227340207 0.0, -88.3053719176 40.1231870207 0.0, -88.3053269176 40.1238850207 0.0, -88.3052989176 40.1241300207 0.0, -88.3052259176 40.1245050207 0.0, -88.3051379176 40.1249760207 0.0, -88.3050759176 40.1253110207 0.0, -88.3048949176 40.1260250207 0.0, -88.3046799176 40.1266320207 0.0, -88.3045409176 40.1269800207 0.0, -88.3043019176 40.1274880207 0.0, -88.3042449176 40.1275830207 0.0, -88.3042009176 40.1276570207 0.0, -88.3040549176 40.1279130207 0.0, -88.3037979176 40.1283440207 0.0, -88.3036779176 40.1285130207 0.0, -88.3033819176 40.1289380207 0.0, -88.3032249176 40.1291360207 0.0, -88.3030239176 40.1293880207 0.0, -88.3025739176 40.1298810207 0.0, -88.3019019176 40.1305270207 0.0, -88.3014599176 40.1309200207 0.0, -88.3001959176 40.1320420207 0.0, -88.2999729176 40.1322340207 0.0, -88.2997759176 40.1324050207 0.0, -88.2994579176 40.1326820207 0.0, -88.2985299176 40.1334890207 0.0, -88.2978749176 40.1341370207 0.0, -88.2967059176 40.1351600207 0.0, -88.2966629176 40.1351300207 0.0, -88.2965229176 40.1350630207 0.0, -88.2963609176 40.1350020207 0.0, -88.2958099176 40.1347960207 0.0, -88.2958179176 40.1352370207 0.0, -88.2943939176 40.1358130207 0.0, -88.2943939176 40.1367050207 0.0, -88.2944169176 40.1368850207 0.0, -88.2913469176 40.1395800207 0.0, -88.2912009176 40.1395150207 0.0, -88.2810139176 40.1357330207 0.0, -88.2809779176 40.1360280207 0.0, -88.2809899176 40.1362730207 0.0, -88.2811399176 40.1394360207 0.0, -88.2809729176 40.1394050207 0.0, -88.2802859175 40.1392910207 0.0, -88.2796899175 40.1392220207 0.0, -88.2790659175 40.1391720207 0.0, -88.2786039175 40.1391610207 0.0, -88.2778299175 40.1391950207 0.0, -88.2776479175 40.1392120207 0.0, -88.2773009175 40.1392430207 0.0, -88.2771369175 40.1392560207 0.0, -88.2771959175 40.1400080207 0.0, -88.2772299175 40.1404850207 0.0, -88.2772389175 40.1406100207 0.0, -88.2772459175 40.1407720207 0.0, -88.2772529175 40.1408690207 0.0, -88.2772669175 40.1410060207 0.0, -88.2772789175 40.1412380207 0.0, -88.2772849175 40.1413740207 0.0, -88.2772949175 40.1415890207 0.0, -88.2773319175 40.1424340207 0.0, -88.2773399175 40.1426350207 0.0, -88.2773379175 40.1431240207 0.0, -88.2773359175 40.1434420207 0.0, -88.2773199175 40.1438910207 0.0, -88.2773069175 40.1440080207 0.0, -88.2753389175 40.1439870207 0.0, -88.2746949175 40.1440130207 0.0, -88.2747779175 40.1433100207 0.0, -88.2749449175 40.1424430207 0.0, -88.2747219175 40.1424260207 0.0, -88.2749119175 40.1416220207 0.0, -88.2745829175 40.1397990207 0.0, -88.2744949175 40.1395030207 0.0, -88.2745219175 40.1394020207 0.0, -88.2745499175 40.1393040207 0.0, -88.2745629175 40.1392630207 0.0, -88.2744159175 40.1391920207 0.0, -88.2744179175 40.1392390207 0.0, -88.2729589175 40.1386070207 0.0, -88.2720249175 40.1382330207 0.0, -88.2719829175 40.1383200207 0.0, -88.2719559175 40.1383980207 0.0, -88.2719029175 40.1386390207 0.0, -88.2715559175 40.1385030207 0.0, -88.2715809175 40.1382590207 0.0, -88.2715809175 40.1381600207 0.0, -88.2715859175 40.1380590207 0.0, -88.2713359175 40.1379670207 0.0, -88.2713169175 40.1380660207 0.0, -88.2712959175 40.1381660207 0.0, -88.2712519175 40.1383840207 0.0, -88.2711839175 40.1387650207 0.0, -88.2705519175 40.1384010207 0.0, -88.2706749175 40.1381770207 0.0, -88.2706349175 40.1379170207 0.0, -88.2705809175 40.1378980207 0.0, -88.2704619175 40.1378560207 0.0, -88.2704509175 40.1377510207 0.0, -88.2702489175 40.1376720207 0.0, -88.2702179175 40.1377760207 0.0, -88.2701579175 40.1379960207 0.0, -88.2704839175 40.1381100207 0.0, -88.2703249175 40.1384920207 0.0, -88.2699079175 40.1383270207 0.0, -88.2701109175 40.1379790207 0.0, -88.2699379175 40.1379180207 0.0))')) }
+ error_geographic_item { GeographicItem.new(geography: RSPEC_GEO_FACTORY.parse_wkt('POLYGON ((-88.2699379175 40.1379180207 0.0, -88.2694189175 40.1375100207 0.0, -88.2694509175 40.1374060207 0.0, -88.2690329175 40.1372730207 0.0, -88.2689819175 40.1373630207 0.0, -88.2689439175 40.1375880207 0.0, -88.2688339175 40.1401300207 0.0, -88.2691719175 40.1415490207 0.0, -88.2691719175 40.1416530207 0.0, -88.2691599175 40.1417120207 0.0, -88.2691429175 40.1419090207 0.0, -88.2695809175 40.1418370207 0.0, -88.2695859175 40.1420250207 0.0, -88.2693509175 40.1420950207 0.0, -88.2693799175 40.1421570207 0.0, -88.2692849175 40.1422310207 0.0, -88.2691779175 40.1431160207 0.0, -88.2696049175 40.1434840207 0.0, -88.2697659175 40.1435510207 0.0, -88.2702859175 40.1432970207 0.0, -88.2712409175 40.1436270207 0.0, -88.2711889175 40.1437380207 0.0, -88.2711549175 40.1438090207 0.0, -88.2716769175 40.1438240207 0.0, -88.2715969175 40.1439420207 0.0, -88.2719599175 40.1441460207 0.0, -88.2723289175 40.1443600207 0.0, -88.2724789175 40.1441270207 0.0, -88.2725889175 40.1441600207 0.0, -88.2725049175 40.1444260207 0.0, -88.2727769175 40.1444800207 0.0, -88.2727069175 40.1446540207 0.0, -88.2726349175 40.1450000207 0.0, -88.2725559175 40.1450000207 0.0, -88.2719339175 40.1447160207 0.0, -88.2715329175 40.1445010207 0.0, -88.2702929175 40.1441310207 0.0, -88.2703029175 40.1447020207 0.0, -88.2703059175 40.1448660207 0.0, -88.2709589175 40.1450450207 0.0, -88.2719749175 40.1453970207 0.0, -88.2720249175 40.1461770207 0.0, -88.2745079175 40.1460990207 0.0, -88.2747479175 40.1463620207 0.0, -88.2749159175 40.1464850207 0.0, -88.2750759175 40.1465150207 0.0, -88.2753009175 40.1465190207 0.0, -88.2752799175 40.1472240207 0.0, -88.2769659175 40.1473490207 0.0, -88.2769669175 40.1476430207 0.0, -88.2752829175 40.1476010207 0.0, -88.2749959175 40.1487340207 0.0, -88.2758409175 40.1487230207 0.0, -88.2764749175 40.1487280207 0.0, -88.2769719175 40.1487050207 0.0, -88.2808679176 40.1481790207 0.0, -88.2814679176 40.1477910207 0.0, -88.2818739176 40.1475280207 0.0, -88.2822109176 40.1468660207 0.0, -88.2824339176 40.1462690207 0.0, -88.2823879176 40.1455250207 0.0, -88.2823329176 40.1451960207 0.0, -88.2823939176 40.1450440207 0.0, -88.2824309176 40.1449460207 0.0, -88.2824749176 40.1448500207 0.0, -88.2827829176 40.1444400207 0.0, -88.2830389176 40.1444320207 0.0, -88.2832379176 40.1444260207 0.0, -88.2842419176 40.1443110207 0.0, -88.2848379176 40.1442640207 0.0, -88.2854609176 40.1441730207 0.0, -88.2858139176 40.1440440207 0.0, -88.2862149176 40.1437670207 0.0, -88.2864479176 40.1436000207 0.0, -88.2867319176 40.1434570207 0.0, -88.2869249176 40.1434520207 0.0, -88.2870509176 40.1435230207 0.0, -88.2871169176 40.1435490207 0.0, -88.2882049176 40.1439130207 0.0, -88.2877929176 40.1448710207 0.0, -88.2873729176 40.1472920207 0.0, -88.2875759176 40.1478710207 0.0, -88.2935339176 40.1499700207 0.0, -88.2951569176 40.1507950207 0.0, -88.2959119176 40.1511640207 0.0, -88.2959339176 40.1517930207 0.0, -88.2959339176 40.1524080207 0.0, -88.2959759176 40.1527150207 0.0, -88.2960249176 40.1528760207 0.0, -88.2961019176 40.1530510207 0.0, -88.2962069176 40.1532250207 0.0, -88.2963419176 40.1534130207 0.0, -88.2964829176 40.1535680207 0.0, -88.2967669176 40.1538350207 0.0, -88.2969229176 40.1539610207 0.0, -88.2980319176 40.1549000207 0.0, -88.2983929176 40.1552160207 0.0, -88.2985249176 40.1553680207 0.0, -88.2986509176 40.1555360207 0.0, -88.2987639176 40.1557180207 0.0, -88.2988309176 40.1558470207 0.0, -88.2989079176 40.1560360207 0.0, -88.2989679176 40.1562850207 0.0, -88.2989699176 40.1567430207 0.0, -88.2989309176 40.1569900207 0.0, -88.2988409176 40.1572420207 0.0, -88.2987769176 40.1573600207 0.0, -88.2986679176 40.1575260207 0.0, -88.2985249176 40.1577090207 0.0, -88.2983469176 40.1579130207 0.0, -88.2981109176 40.1581830207 0.0, -88.2980459176 40.1582610207 0.0, -88.2979819176 40.1583370207 0.0, -88.2961539176 40.1567660207 0.0, -88.2937499176 40.1547220207 0.0, -88.2931789176 40.1542330207 0.0, -88.2876939176 40.1495810207 0.0, -88.2876389176 40.1497300207 0.0, -88.2875779176 40.1499350207 0.0, -88.2830889176 40.1496570207 0.0, -88.2813489176 40.1494580207 0.0, -88.2802749175 40.1494620207 0.0, -88.2800369175 40.1494750207 0.0, -88.2797929175 40.1494780207 0.0, -88.2769839175 40.1495000207 0.0, -88.2757809175 40.1495950207 0.0, -88.2759029175 40.1504800207 0.0, -88.2769959175 40.1504800207 0.0, -88.2770079175 40.1516580207 0.0, -88.2770129175 40.1519290207 0.0, -88.2770149175 40.1520720207 0.0, -88.2770189175 40.1523070207 0.0, -88.2762159175 40.1530050207 0.0, -88.2745889175 40.1544400207 0.0, -88.2746399175 40.1546200207 0.0, -88.2748599175 40.1563480207 0.0, -88.2749489175 40.1576120207 0.0, -88.2749549175 40.1576710207 0.0, -88.2749519175 40.1577340207 0.0, -88.2749459175 40.1579400207 0.0, -88.2758939175 40.1578840207 0.0, -88.2770359175 40.1578730207 0.0, -88.2770399175 40.1577900207 0.0, -88.2770439175 40.1577240207 0.0, -88.2770449175 40.1576500207 0.0, -88.2770609175 40.1563080207 0.0, -88.2770729175 40.1559180207 0.0, -88.2778969175 40.1556830207 0.0, -88.2789139175 40.1555090207 0.0, -88.2806589176 40.1555130207 0.0, -88.2844099176 40.1578440207 0.0, -88.2832309176 40.1585310207 0.0, -88.2831939176 40.1624750208 0.0, -88.2779909175 40.1624900208 0.0, -88.2779959175 40.1616910208 0.0, -88.2771059175 40.1617370208 0.0, -88.2769059175 40.1617340208 0.0, -88.2768959175 40.1593910207 0.0, -88.2694489175 40.1590980207 0.0, -88.2692729175 40.1590910207 0.0, -88.2692909175 40.1588480207 0.0, -88.2693799175 40.1586100207 0.0, -88.2694969175 40.1583720207 0.0, -88.2697069175 40.1581600207 0.0, -88.2696089175 40.1581600207 0.0, -88.2692929175 40.1581590207 0.0, -88.2670149175 40.1575690207 0.0, -88.2667059175 40.1574360207 0.0, -88.2657079175 40.1570060207 0.0, -88.2647769175 40.1566580207 0.0, -88.2647769175 40.1565960207 0.0, -88.2647829175 40.1564650207 0.0, -88.2664309175 40.1556340207 0.0, -88.2658499175 40.1556350207 0.0, -88.2658569175 40.1539210207 0.0, -88.2664319175 40.1539200207 0.0, -88.2664329175 40.1525100207 0.0, -88.2665769175 40.1525140207 0.0, -88.2691989175 40.1525300207 0.0, -88.2691929175 40.1520630207 0.0, -88.2691889175 40.1517860207 0.0, -88.2666259175 40.1517110207 0.0, -88.2664969175 40.1517090207 0.0, -88.2657859175 40.1517370207 0.0, -88.2657179175 40.1509970207 0.0, -88.2665079175 40.1510350207 0.0, -88.2664769175 40.1500330207 0.0, -88.2664669175 40.1498330207 0.0, -88.2664509175 40.1484940207 0.0, -88.2664209175 40.1469820207 0.0, -88.2664229175 40.1467290207 0.0, -88.2664219175 40.1461990207 0.0, -88.2660819175 40.1462060207 0.0, -88.2657829175 40.1462120207 0.0, -88.2645659175 40.1464580207 0.0, -88.2619779175 40.1469820207 0.0, -88.2619819175 40.1473840207 0.0, -88.2579499175 40.1469560207 0.0, -88.2579399175 40.1465160207 0.0, -88.2579189175 40.1452580207 0.0, -88.2575909175 40.1451250207 0.0, -88.2564839175 40.1451320207 0.0, -88.2488059175 40.1451920207 0.0, -88.2485279175 40.1451930207 0.0, -88.2485169175 40.1456130207 0.0, -88.2485279175 40.1460380207 0.0, -88.2482809175 40.1460320207 0.0, -88.2482809175 40.1462550207 0.0, -88.2480509175 40.1462550207 0.0, -88.2480509175 40.1468130207 0.0, -88.2478519175 40.1468130207 0.0, -88.2467419175 40.1468130207 0.0, -88.2465069175 40.1468130207 0.0, -88.2465069175 40.1460320207 0.0, -88.2456309175 40.1460380207 0.0, -88.2456289175 40.1456130207 0.0, -88.2455699175 40.1443560207 0.0, -88.2454669175 40.1444120207 0.0, -88.2453489175 40.1444350207 0.0, -88.2452319175 40.1444460207 0.0, -88.2441499175 40.1444410207 0.0, -88.2408969175 40.1444130207 0.0, -88.2407789175 40.1444020207 0.0, -88.2406979175 40.1443960207 0.0, -88.2405949175 40.1443740207 0.0, -88.2390569175 40.1444070207 0.0, -88.2387699175 40.1444300207 0.0, -88.2389189175 40.1493700207 0.0, -88.2391769175 40.1563040207 0.0, -88.2392089175 40.1600920207 0.0, -88.2318049175 40.1600520207 0.0, -88.2272139175 40.1599860207 0.0, -88.2271789175 40.1607360207 0.0, -88.2271999175 40.1641090208 0.0, -88.2244449175 40.1641480208 0.0, -88.2208259175 40.1641200208 0.0, -88.2222659175 40.1606850207 0.0, -88.2241799175 40.1563300207 0.0, -88.2260719175 40.1520830207 0.0, -88.2275839175 40.1485910207 0.0, -88.2290229175 40.1450520207 0.0, -88.2326749175 40.1449470207 0.0, -88.2326439175 40.1438400207 0.0, -88.2357619175 40.1437390207 0.0, -88.2378949175 40.1437310207 0.0, -88.2390439175 40.1436920207 0.0, -88.2390419175 40.1435950207 0.0, -88.2390229175 40.1426800207 0.0, -88.2390129175 40.1417560207 0.0, -88.2378879175 40.1417440207 0.0, -88.2358699175 40.1417590207 0.0, -88.2358939175 40.1411200207 0.0, -88.2361929175 40.1411060207 0.0, -88.2364019175 40.1396900207 0.0, -88.2362729175 40.1396440207 0.0, -88.2358119175 40.1379660207 0.0, -88.2358259175 40.1351040207 0.0, -88.2330329175 40.1351040207 0.0, -88.2327739175 40.1351080207 0.0, -88.2326309175 40.1351070207 0.0, -88.2325349175 40.1351070207 0.0, -88.2325989175 40.1349510207 0.0, -88.2326259175 40.1348880207 0.0, -88.2326679175 40.1347920207 0.0, -88.2327539175 40.1347310207 0.0, -88.2328059175 40.1345940207 0.0, -88.2335779175 40.1328740207 0.0, -88.2338679175 40.1324320207 0.0, -88.2341199175 40.1317740207 0.0, -88.2342589175 40.1317990207 0.0, -88.2344729175 40.1317910207 0.0, -88.2356249175 40.1318060207 0.0, -88.2356379175 40.1309200207 0.0, -88.2346849175 40.1304570207 0.0, -88.2341559175 40.1309960207 0.0, -88.2340829175 40.1309900207 0.0, -88.2339849175 40.1309900207 0.0, -88.2335879175 40.1309900207 0.0, -88.2299539175 40.1310020207 0.0, -88.2290679175 40.1309360207 0.0, -88.2290699175 40.1305940207 0.0, -88.2290699175 40.1293820207 0.0, -88.2281079175 40.1293810207 0.0, -88.2281079175 40.1290110207 0.0, -88.2267949175 40.1289690207 0.0, -88.2262929175 40.1289730207 0.0, -88.2262929175 40.1286210207 0.0, -88.2256409175 40.1286220207 0.0, -88.2252769175 40.1286210207 0.0, -88.2259989175 40.1277740207 0.0, -88.2259809175 40.1274940207 0.0, -88.2259709175 40.1273620207 0.0, -88.2267119175 40.1273540207 0.0, -88.2276389175 40.1273430207 0.0, -88.2284139175 40.1273430207 0.0, -88.2285949175 40.1273420207 0.0, -88.2288739175 40.1273390207 0.0, -88.2288719175 40.1262580207 0.0, -88.2288989175 40.1260710207 0.0, -88.2289069175 40.1258470207 0.0, -88.2280059175 40.1258480207 0.0, -88.2279919175 40.1250810207 0.0, -88.2279839175 40.1246830207 0.0, -88.2288669175 40.1246850207 0.0, -88.2289819175 40.1246900207 0.0, -88.2289699175 40.1238710207 0.0, -88.2289639175 40.1236620207 0.0, -88.2288569175 40.1238470207 0.0, -88.2288509175 40.1231300207 0.0, -88.2288459175 40.1226230207 0.0, -88.2288369175 40.1221790207 0.0, -88.2288319175 40.1212280207 0.0, -88.2288349175 40.1207790207 0.0, -88.2288219175 40.1202710207 0.0, -88.2288009175 40.1193220207 0.0, -88.2287969175 40.1191140207 0.0, -88.2288029175 40.1189820207 0.0, -88.2287979175 40.1189340207 0.0, -88.2287969175 40.1188370207 0.0, -88.2287939175 40.1183480207 0.0, -88.2287849175 40.1173860207 0.0, -88.2287709175 40.1163870207 0.0, -88.2288599175 40.1163890207 0.0, -88.2288259175 40.1135150207 0.0, -88.2288169175 40.1127440207 0.0, -88.2288119175 40.1122470207 0.0, -88.2287969175 40.1111530207 0.0, -88.2288239175 40.1103320207 0.0, -88.2287579175 40.1091430207 0.0, -88.2287269175 40.1059510207 0.0, -88.2287249175 40.1054870207 0.0, -88.2289289175 40.1054880207 0.0, -88.2288799175 40.1041940207 0.0, -88.2288539175 40.1030760207 0.0, -88.2288539175 40.1022540207 0.0, -88.2288339175 40.1014970207 0.0, -88.2287329175 40.1014980207 0.0, -88.2286329175 40.1014980207 0.0, -88.2287219175 40.1014850207 0.0, -88.2288519175 40.1014780207 0.0, -88.2288369175 40.1005780207 0.0, -88.2301899175 40.1005660207 0.0, -88.2310459175 40.1005600207 0.0, -88.2325089175 40.1005560207 0.0, -88.2333939175 40.1005460207 0.0, -88.2333699175 40.0990220207 0.0, -88.2333399175 40.0981690207 0.0, -88.2333359175 40.0980650207 0.0, -88.2328219175 40.0980480207 0.0, -88.2310829175 40.0980610207 0.0, -88.2286919175 40.0980650207 0.0, -88.2286569175 40.0959180207 0.0, -88.2285969175 40.0955070207 0.0, -88.2311059175 40.0954430207 0.0, -88.2312349175 40.0954860207 0.0, -88.2332969175 40.0955490207 0.0, -88.2347279175 40.0954610207 0.0, -88.2357119175 40.0953780207 0.0, -88.2362889175 40.0953390207 0.0, -88.2363399175 40.0953160207 0.0, -88.2385729175 40.0952720207 0.0, -88.2414529175 40.0952030207 0.0, -88.2443629175 40.0952050207 0.0, -88.2445099175 40.0944610207 0.0, -88.2447989175 40.0914670207 0.0, -88.2448509175 40.0909330207 0.0, -88.2446249175 40.0909290207 0.0, -88.2446399175 40.0907560207 0.0, -88.2438699175 40.0907430207 0.0, -88.2438389175 40.0908980207 0.0, -88.2436379175 40.0908990207 0.0, -88.2435849175 40.0912980207 0.0, -88.2435939175 40.0914820207 0.0, -88.2433909175 40.0914770207 0.0, -88.2432299175 40.0914750207 0.0, -88.2432179175 40.0909720207 0.0, -88.2431949175 40.0908700207 0.0, -88.2431499175 40.0908380207 0.0, -88.2430579175 40.0908220207 0.0, -88.2430379175 40.0908220207 0.0, -88.2429919175 40.0874370207 0.0, -88.2448279175 40.0870540207 0.0, -88.2452799175 40.0870570207 0.0, -88.2455489175 40.0870620207 0.0, -88.2457899175 40.0852100207 0.0, -88.2459049175 40.0852570207 0.0, -88.2460559175 40.0841200207 0.0, -88.2460759175 40.0840090207 0.0, -88.2461039175 40.0838200207 0.0, -88.2461319175 40.0835820207 0.0, -88.2461429175 40.0835100207 0.0, -88.2464089175 40.0835120207 0.0, -88.2465539175 40.0835100207 0.0, -88.2468669175 40.0835090207 0.0, -88.2484209175 40.0835060207 0.0, -88.2490619175 40.0834990207 0.0, -88.2493419175 40.0834840207 0.0, -88.2494179175 40.0835610207 0.0, -88.2496439175 40.0835600207 0.0, -88.2506609175 40.0835640207 0.0, -88.2511039175 40.0835630207 0.0, -88.2516819175 40.0835750207 0.0, -88.2527409175 40.0835860207 0.0, -88.2537559175 40.0835860207 0.0, -88.2547969175 40.0835940207 0.0, -88.2574609175 40.0836170207 0.0, -88.2574479175 40.0826020207 0.0, -88.2571489175 40.0826190207 0.0, -88.2571319175 40.0822840207 0.0, -88.2574439175 40.0822820207 0.0, -88.2574339175 40.0814870207 0.0, -88.2574209175 40.0806940207 0.0, -88.2574219175 40.0803910207 0.0, -88.2574219175 40.0802720207 0.0, -88.2574159175 40.0792300207 0.0, -88.2574169175 40.0791180207 0.0, -88.2574069175 40.0780780207 0.0, -88.2574039175 40.0775480207 0.0, -88.2573989175 40.0771140207 0.0, -88.2573949175 40.0768220207 0.0, -88.2577309175 40.0766840207 0.0, -88.2614989175 40.0767370207 0.0, -88.2619489175 40.0765900207 0.0, -88.2707989175 40.0764250207 0.0, -88.2715599175 40.0768370207 0.0, -88.2715239175 40.0836710207 0.0, -88.2708329175 40.0836600207 0.0, -88.2706829175 40.0836610207 0.0, -88.2706089175 40.0836690207 0.0, -88.2698599175 40.0836640207 0.0, -88.2686649175 40.0836570207 0.0, -88.2674419175 40.0836550207 0.0, -88.2670449175 40.0836540207 0.0, -88.2669639175 40.0850200207 0.0, -88.2673559175 40.0850510207 0.0, -88.2674739175 40.0842260207 0.0, -88.2678509175 40.0842190207 0.0, -88.2677159175 40.0865060207 0.0, -88.2677739175 40.0872270207 0.0, -88.2694399175 40.0872250207 0.0, -88.2699469175 40.0872430207 0.0, -88.2701829175 40.0873020207 0.0, -88.2700759175 40.0868960207 0.0, -88.2705019175 40.0868950207 0.0, -88.2704909175 40.0873010207 0.0, -88.2720569175 40.0871960207 0.0, -88.2753329175 40.0872240207 0.0, -88.2763729175 40.0873340207 0.0, -88.2763719175 40.0872410207 0.0, -88.2759969175 40.0870250207 0.0, -88.2756689175 40.0868530207 0.0, -88.2754079175 40.0869000207 0.0, -88.2755299175 40.0865950207 0.0, -88.2756119175 40.0865940207 0.0, -88.2758459175 40.0866120207 0.0, -88.2763699175 40.0870460207 0.0, -88.2763689175 40.0869230207 0.0, -88.2763599175 40.0863190207 0.0, -88.2763469175 40.0855380207 0.0, -88.2763199175 40.0837030207 0.0, -88.2763149175 40.0834320207 0.0, -88.2763049175 40.0825170207 0.0, -88.2773889175 40.0826780207 0.0, -88.2774189175 40.0834280207 0.0, -88.2790239175 40.0834610207 0.0, -88.2796329175 40.0832160207 0.0, -88.2796279175 40.0824130207 0.0, -88.2790189175 40.0824070207 0.0, -88.2789969175 40.0820510207 0.0, -88.2796269175 40.0820760207 0.0, -88.2796269175 40.0819610207 0.0, -88.2795989175 40.0817230207 0.0, -88.2795349175 40.0815780207 0.0, -88.2794339175 40.0814360207 0.0, -88.2793659175 40.0813720207 0.0, -88.2792849175 40.0813100207 0.0, -88.2791449175 40.0812470207 0.0, -88.2788889175 40.0811780207 0.0, -88.2787939175 40.0811660207 0.0, -88.2769879175 40.0811460207 0.0, -88.2763829175 40.0811170207 0.0, -88.2762909175 40.0811140207 0.0, -88.2762869175 40.0804170207 0.0, -88.2762829175 40.0800160207 0.0, -88.2762749175 40.0791340207 0.0, -88.2761879175 40.0740310207 0.0, -88.2811179176 40.0740120207 0.0, -88.2811259176 40.0734400207 0.0, -88.2810779176 40.0733920207 0.0, -88.2810409176 40.0733330207 0.0, -88.2809999176 40.0730960207 0.0, -88.2809869176 40.0728640207 0.0, -88.2810029176 40.0723840207 0.0, -88.2809729176 40.0709380207 0.0, -88.2809549176 40.0699920207 0.0, -88.2809569176 40.0692390207 0.0, -88.2832339176 40.0692580207 0.0, -88.2883059176 40.0693170207 0.0, -88.2883429176 40.0707940207 0.0, -88.2889439176 40.0748830207 0.0, -88.2891619176 40.0758480207 0.0, -88.2890199176 40.0765390207 0.0, -88.2864479176 40.0768720207 0.0, -88.2851129176 40.0763130207 0.0, -88.2833079176 40.0757600207 0.0, -88.2833039176 40.0761750207 0.0, -88.2806479176 40.0761960207 0.0, -88.2807249176 40.0793360207 0.0, -88.2808479176 40.0802050207 0.0, -88.2808089176 40.0804980207 0.0, -88.2807029176 40.0835500207 0.0, -88.2811879176 40.0834920207 0.0, -88.2861959176 40.0835110207 0.0, -88.2864749176 40.0834730207 0.0, -88.2864739176 40.0836800207 0.0, -88.2872489176 40.0836740207 0.0, -88.2872629176 40.0838470207 0.0, -88.2877869176 40.0838530207 0.0, -88.2900159176 40.0838710207 0.0, -88.2924959176 40.0838940207 0.0, -88.2934389176 40.0839040207 0.0, -88.2937569176 40.0839100207 0.0, -88.2949579176 40.0839300207 0.0, -88.2950099176 40.0815760207 0.0, -88.2950009176 40.0812920207 0.0, -88.2945229176 40.0812970207 0.0, -88.2945719176 40.0811280207 0.0, -88.2945549176 40.0808880207 0.0, -88.2942109176 40.0808580207 0.0, -88.2934689176 40.0807720207 0.0, -88.2930259176 40.0807400207 0.0, -88.2927119176 40.0799070207 0.0, -88.2932579176 40.0788420207 0.0, -88.2930859176 40.0775430207 0.0, -88.2920859176 40.0777210207 0.0, -88.2921809176 40.0807530207 0.0, -88.2915949176 40.0807280207 0.0, -88.2911109176 40.0807490207 0.0, -88.2912309176 40.0796550207 0.0, -88.2905429176 40.0797080207 0.0, -88.2905399176 40.0794290207 0.0, -88.2910349176 40.0793940207 0.0, -88.2909229176 40.0791250207 0.0, -88.2910899176 40.0790960207 0.0, -88.2911999176 40.0776420207 0.0, -88.2916249176 40.0777320207 0.0, -88.2916159176 40.0774940207 0.0, -88.2914409176 40.0775040207 0.0, -88.2912349176 40.0774960207 0.0, -88.2912199176 40.0772390207 0.0, -88.2914359176 40.0772770207 0.0, -88.2918549176 40.0771280207 0.0, -88.2924579176 40.0768630207 0.0, -88.2931839176 40.0768260207 0.0, -88.2931629176 40.0770640207 0.0, -88.2931709176 40.0771770207 0.0, -88.2938129176 40.0771740207 0.0, -88.2938249176 40.0772890207 0.0, -88.2939089176 40.0773730207 0.0, -88.2940999176 40.0773700207 0.0, -88.2941249176 40.0773270207 0.0, -88.2941359176 40.0772560207 0.0, -88.2941299176 40.0771730207 0.0, -88.2949449176 40.0771780207 0.0, -88.2949379176 40.0758310207 0.0, -88.2951109176 40.0758290207 0.0, -88.3037509176 40.0758370207 0.0, -88.3037569176 40.0764130207 0.0, -88.3038759176 40.0764150207 0.0, -88.3039719176 40.0764120207 0.0, -88.3040979176 40.0764140207 0.0, -88.3041179176 40.0776810207 0.0, -88.3041569176 40.0783820207 0.0, -88.3042289176 40.0792890207 0.0, -88.3043149176 40.0801260207 0.0, -88.3043919176 40.0807620207 0.0, -88.3137929176 40.0808050207 0.0, -88.3140469176 40.0808320207 0.0, -88.3141029176 40.0841320207 0.0, -88.3124959176 40.0841250207 0.0, -88.3108159176 40.0841070207 0.0, -88.3080419176 40.0840740207 0.0, -88.3071149176 40.0840540207 0.0, -88.3050069176 40.0840260207 0.0, -88.3047409176 40.0840250207 0.0, -88.3046679176 40.0840240207 0.0, -88.3045639176 40.0840240207 0.0, -88.3044009176 40.0840230207 0.0, -88.3041979176 40.0840220207 0.0, -88.3016319176 40.0839970207 0.0, -88.3005509176 40.0839900207 0.0, -88.3004489176 40.0839890207 0.0, -88.2987589176 40.0839730207 0.0, -88.2987639176 40.0841020207 0.0, -88.2980169176 40.0849980207 0.0, -88.2978809176 40.0852280207 0.0, -88.2977539176 40.0854380207 0.0, -88.2975069176 40.0854640207 0.0, -88.2974039176 40.0854580207 0.0, -88.2964029176 40.0854640207 0.0, -88.2951779176 40.0853020207 0.0, -88.2951849176 40.0863370207 0.0, -88.2951889176 40.0866230207 0.0, -88.2951939176 40.0878100207 0.0, -88.2951949176 40.0886710207 0.0, -88.2952009176 40.0897370207 0.0, -88.2952109176 40.0908150207 0.0, -88.2952139176 40.0911630207 0.0, -88.2952259176 40.0933020207 0.0, -88.2952289176 40.0951230207 0.0, -88.2952259176 40.0960230207 0.0, -88.2952339176 40.0976360207 0.0, -88.2952419176 40.0984640207 0.0, -88.2952449176 40.0992210207 0.0, -88.2952449176 40.0999440207 0.0, -88.2952559176 40.1012930207 0.0, -88.2946119176 40.1012930207 0.0, -88.2946289176 40.1015740207 0.0, -88.2952579176 40.1015690207 0.0, -88.2952629176 40.1020760207 0.0, -88.2952729176 40.1024860207 0.0, -88.2952789176 40.1027510207 0.0, -88.2952829176 40.1035260207 0.0, -88.2953169176 40.1056980207 0.0, -88.2970009176 40.1057200207 0.0, -88.2986299176 40.1057860207 0.0, -88.3021129176 40.1058270207 0.0, -88.3022179176 40.1057470207 0.0, -88.3040989176 40.1056900207 0.0, -88.3040869176 40.1061910207 0.0, -88.3047309176 40.1059720207 0.0, -88.3048539176 40.1059780207 0.0, -88.3048439176 40.1063560207 0.0, -88.3049229176 40.1063550207 0.0, -88.3049209176 40.1055820207 0.0, -88.3049089176 40.1027780207 0.0, -88.3050309176 40.1027750207 0.0, -88.3059199176 40.1032180207 0.0, -88.3060289176 40.1032480207 0.0, -88.3093139176 40.1036720207 0.0, -88.3103869176 40.1037630207 0.0, -88.3103899176 40.1037250207 0.0, -88.3104459176 40.1035060207 0.0, -88.3105409176 40.1033270207 0.0, -88.3107279176 40.1031130207 0.0, -88.3109389176 40.1029720207 0.0, -88.3111269176 40.1028760207 0.0, -88.3113269176 40.1028040207 0.0, -88.3115879176 40.1027560207 0.0, -88.3119299176 40.1027430207 0.0, -88.3123049176 40.1027550207 0.0, -88.3125959176 40.1028050207 0.0, -88.3127629176 40.1028660207 0.0, -88.3129009176 40.1029490207 0.0, -88.3130689176 40.1030790207 0.0, -88.3131209176 40.1031290207 0.0, -88.3132189176 40.1032320207 0.0, -88.3143519176 40.1027170207 0.0, -88.3143639176 40.1035540207 0.0, -88.3144049176 40.1058550207 0.0, -88.3144219176 40.1065620207 0.0, -88.3144389176 40.1077600207 0.0, -88.3144639176 40.1088000207 0.0, -88.3144709176 40.1091040207 0.0, -88.3144769176 40.1094420207 0.0, -88.3172839176 40.1092760207 0.0, -88.3172929176 40.1089890207 0.0, -88.3173259176 40.1058900207 0.0, -88.3180159176 40.1058690207 0.0, -88.3215459176 40.1058690207 0.0, -88.3215489176 40.1124680207 0.0, -88.3215439176 40.1126830207 0.0, -88.3145259176 40.1128000207 0.0, -88.3141369176 40.1128220207 0.0, -88.3141709176 40.1111100207 0.0, -88.3141889176 40.1104590207 0.0, -88.3141759176 40.1101010207 0.0, -88.3127929176 40.1100220207 0.0, -88.3117729176 40.1099990207 0.0, -88.3115649176 40.1088250207 0.0, -88.3111399176 40.1087850207 0.0, -88.3112019176 40.1082790207 0.0, -88.3063529176 40.1083230207 0.0, -88.3058249176 40.1083240207 0.0, -88.3058309176 40.1087750207 0.0, -88.3058459176 40.1100150207 0.0, -88.3057469176 40.1100120207 0.0, -88.3051759176 40.1099940207 0.0, -88.3051109176 40.1099930207 0.0, -88.3051659176 40.1124020207 0.0, -88.3051639176 40.1126760207 0.0, -88.3049909176 40.1126710207 0.0, -88.3048239176 40.1126710207 0.0, -88.3044389176 40.1126730207 0.0, -88.3034609176 40.1126980207 0.0, -88.2978629176 40.1126260207 0.0, -88.2963299176 40.1126490207 0.0, -88.2954549176 40.1126340207 0.0, -88.2954549176 40.1127350207 0.0, -88.2941329176 40.1129920207 0.0, -88.2941169176 40.1130370207 0.0, -88.2939439176 40.1130300207 0.0, -88.2938339176 40.1130550207 0.0, -88.2928849176 40.1132110207 0.0, -88.2925109176 40.1134080207 0.0, -88.2925619176 40.1135620207 0.0, -88.2928599176 40.1134400207 0.0, -88.2932559176 40.1133030207 0.0, -88.2930019176 40.1135000207 0.0, -88.2926379176 40.1137090207 0.0, -88.2918009176 40.1148490207 0.0, -88.2918689176 40.1148270207 0.0, -88.2919799176 40.1148060207 0.0, -88.2923059176 40.1147710207 0.0, -88.2927259176 40.1147730207 0.0, -88.2943119176 40.1147660207 0.0, -88.2945359176 40.1147810207 0.0, -88.2947229176 40.1148240207 0.0, -88.2948859176 40.1148840207 0.0, -88.2950179176 40.1149220207 0.0, -88.2951359176 40.1149370207 0.0, -88.2954739176 40.1149490207 0.0, -88.2955069176 40.1175490207 0.0, -88.2955509176 40.1195370207 0.0, -88.2955539176 40.1197840207 0.0, -88.2955559176 40.1200370207 0.0, -88.2973569176 40.1200550207 0.0, -88.2985229176 40.1200660207 0.0, -88.2985139176 40.1198080207 0.0, -88.2985169176 40.1196960207 0.0, -88.2985349176 40.1194240207 0.0, -88.2984369176 40.1182020207 0.0, -88.2985869176 40.1179400207 0.0, -88.2983599176 40.1176520207 0.0, -88.2985809176 40.1176490207 0.0, -88.2985779176 40.1175450207 0.0, -88.2990509176 40.1175590207 0.0, -88.2990159176 40.1176740207 0.0, -88.3002049176 40.1175390207 0.0, -88.3002199176 40.1182600207 0.0, -88.3002449176 40.1196190207 0.0, -88.3003229176 40.1197840207 0.0, -88.3000039176 40.1197990207 0.0, -88.2996099176 40.1198140207 0.0, -88.2996449176 40.1200750207 0.0, -88.3008319176 40.1200880207 0.0, -88.3006279176 40.1198330207 0.0, -88.3008669176 40.1197590207 0.0, -88.3013149176 40.1197200207 0.0, -88.3014889176 40.1196800207 0.0, -88.3017179176 40.1196090207 0.0, -88.3019389176 40.1195110207 0.0, -88.3020929176 40.1194200207 0.0, -88.3023179176 40.1192110207 0.0, -88.3026989176 40.1187370207 0.0, -88.3028339176 40.1185990207 0.0, -88.3029919176 40.1184770207 0.0, -88.3039059176 40.1180900207 0.0, -88.3040439176 40.1180160207 0.0, -88.3042179176 40.1178890207 0.0, -88.3044189176 40.1176600207 0.0, -88.3045179176 40.1175260207 0.0, -88.3045889176 40.1173920207 0.0, -88.3046499176 40.1172340207 0.0, -88.3046989176 40.1169580207 0.0, -88.3048429176 40.1155740207 0.0, -88.3050059176 40.1154770207 0.0, -88.3051699176 40.1155350207 0.0, -88.3053009176 40.1171280207 0.0, -88.3053309176 40.1172280207 0.0, -88.3053479176 40.1172850207 0.0, -88.3054309176 40.1174630207 0.0, -88.3055399176 40.1176170207 0.0, -88.3056939176 40.1177780207 0.0, -88.3058319176 40.1178890207 0.0, -88.3060489176 40.1180230207 0.0, -88.3067439176 40.1183430207 0.0, -88.3069019176 40.1184410207 0.0, -88.3070229176 40.1185400207 0.0, -88.3074209176 40.1190610207 0.0, -88.3075989176 40.1192740207 0.0, -88.3077139176 40.1193960207 0.0, -88.3078719176 40.1195100207 0.0, -88.3080729176 40.1196130207 0.0, -88.3082609176 40.1196880207 0.0, -88.3083989176 40.1197270207 0.0, -88.3087619176 40.1197980207 0.0, -88.3102029176 40.1199330207 0.0, -88.3104399176 40.1200800207 0.0, -88.3101859176 40.1202000207 0.0, -88.3092619176 40.1202600207 0.0, -88.3087229176 40.1203270207 0.0, -88.3085169176 40.1203790207 0.0, -88.3084079176 40.1204220207 0.0, -88.3083279176 40.1204540207 0.0, -88.3081459176 40.1205520207 0.0, -88.3079489176 40.1206780207 0.0, -88.3077829176 40.1208280207 0.0, -88.3073819176 40.1212900207 0.0, -88.3072519176 40.1214160207 0.0, -88.3071179176 40.1215190207 0.0, -88.3069589176 40.1216130207 0.0, -88.3062069176 40.1219570207 0.0, -88.3059699176 40.1220830207 0.0, -88.3058639176 40.1221580207 0.0, -88.3057209176 40.1222880207 0.0, -88.3056329176 40.1224020207 0.0, -88.3055539176 40.1225330207 0.0, -88.3054549176 40.1227340207 0.0, -88.3053719176 40.1231870207 0.0, -88.3053269176 40.1238850207 0.0, -88.3052989176 40.1241300207 0.0, -88.3052259176 40.1245050207 0.0, -88.3051379176 40.1249760207 0.0, -88.3050759176 40.1253110207 0.0, -88.3048949176 40.1260250207 0.0, -88.3046799176 40.1266320207 0.0, -88.3045409176 40.1269800207 0.0, -88.3043019176 40.1274880207 0.0, -88.3042449176 40.1275830207 0.0, -88.3042009176 40.1276570207 0.0, -88.3040549176 40.1279130207 0.0, -88.3037979176 40.1283440207 0.0, -88.3036779176 40.1285130207 0.0, -88.3033819176 40.1289380207 0.0, -88.3032249176 40.1291360207 0.0, -88.3030239176 40.1293880207 0.0, -88.3025739176 40.1298810207 0.0, -88.3019019176 40.1305270207 0.0, -88.3014599176 40.1309200207 0.0, -88.3001959176 40.1320420207 0.0, -88.2999729176 40.1322340207 0.0, -88.2997759176 40.1324050207 0.0, -88.2994579176 40.1326820207 0.0, -88.2985299176 40.1334890207 0.0, -88.2978749176 40.1341370207 0.0, -88.2967059176 40.1351600207 0.0, -88.2966629176 40.1351300207 0.0, -88.2965229176 40.1350630207 0.0, -88.2963609176 40.1350020207 0.0, -88.2958099176 40.1347960207 0.0, -88.2958179176 40.1352370207 0.0, -88.2943939176 40.1358130207 0.0, -88.2943939176 40.1367050207 0.0, -88.2944169176 40.1368850207 0.0, -88.2913469176 40.1395800207 0.0, -88.2912009176 40.1395150207 0.0, -88.2810139176 40.1357330207 0.0, -88.2809779176 40.1360280207 0.0, -88.2809899176 40.1362730207 0.0, -88.2811399176 40.1394360207 0.0, -88.2809729176 40.1394050207 0.0, -88.2802859175 40.1392910207 0.0, -88.2796899175 40.1392220207 0.0, -88.2790659175 40.1391720207 0.0, -88.2786039175 40.1391610207 0.0, -88.2778299175 40.1391950207 0.0, -88.2776479175 40.1392120207 0.0, -88.2773009175 40.1392430207 0.0, -88.2771369175 40.1392560207 0.0, -88.2771959175 40.1400080207 0.0, -88.2772299175 40.1404850207 0.0, -88.2772389175 40.1406100207 0.0, -88.2772459175 40.1407720207 0.0, -88.2772529175 40.1408690207 0.0, -88.2772669175 40.1410060207 0.0, -88.2772789175 40.1412380207 0.0, -88.2772849175 40.1413740207 0.0, -88.2772949175 40.1415890207 0.0, -88.2773319175 40.1424340207 0.0, -88.2773399175 40.1426350207 0.0, -88.2773379175 40.1431240207 0.0, -88.2773359175 40.1434420207 0.0, -88.2773199175 40.1438910207 0.0, -88.2773069175 40.1440080207 0.0, -88.2753389175 40.1439870207 0.0, -88.2746949175 40.1440130207 0.0, -88.2747779175 40.1433100207 0.0, -88.2749449175 40.1424430207 0.0, -88.2747219175 40.1424260207 0.0, -88.2749119175 40.1416220207 0.0, -88.2745829175 40.1397990207 0.0, -88.2744949175 40.1395030207 0.0, -88.2745219175 40.1394020207 0.0, -88.2745499175 40.1393040207 0.0, -88.2745629175 40.1392630207 0.0, -88.2744159175 40.1391920207 0.0, -88.2744179175 40.1392390207 0.0, -88.2729589175 40.1386070207 0.0, -88.2720249175 40.1382330207 0.0, -88.2719829175 40.1383200207 0.0, -88.2719559175 40.1383980207 0.0, -88.2719029175 40.1386390207 0.0, -88.2715559175 40.1385030207 0.0, -88.2715809175 40.1382590207 0.0, -88.2715809175 40.1381600207 0.0, -88.2715859175 40.1380590207 0.0, -88.2713359175 40.1379670207 0.0, -88.2713169175 40.1380660207 0.0, -88.2712959175 40.1381660207 0.0, -88.2712519175 40.1383840207 0.0, -88.2711839175 40.1387650207 0.0, -88.2705519175 40.1384010207 0.0, -88.2706749175 40.1381770207 0.0, -88.2706349175 40.1379170207 0.0, -88.2705809175 40.1378980207 0.0, -88.2704619175 40.1378560207 0.0, -88.2704509175 40.1377510207 0.0, -88.2702489175 40.1376720207 0.0, -88.2702179175 40.1377760207 0.0, -88.2701579175 40.1379960207 0.0, -88.2704839175 40.1381100207 0.0, -88.2703249175 40.1384920207 0.0, -88.2699079175 40.1383270207 0.0, -88.2701109175 40.1379790207 0.0, -88.2699379175 40.1379180207 0.0))')) }
end
=begin
{
diff --git a/spec/files/shapefiles/2957_projected.dbf b/spec/files/shapefiles/2957_projected.dbf
new file mode 100644
index 0000000000..e2a450aec7
Binary files /dev/null and b/spec/files/shapefiles/2957_projected.dbf differ
diff --git a/spec/files/shapefiles/2957_projected.prj b/spec/files/shapefiles/2957_projected.prj
new file mode 100644
index 0000000000..1616be08d4
--- /dev/null
+++ b/spec/files/shapefiles/2957_projected.prj
@@ -0,0 +1 @@
+PROJCS["NAD_1983_CSRS_UTM_Zone_13N",GEOGCS["GCS_North_American_1983_CSRS",DATUM["D_North_American_1983_CSRS",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-105.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/spec/files/shapefiles/2957_projected.shp b/spec/files/shapefiles/2957_projected.shp
new file mode 100644
index 0000000000..4524abb07c
Binary files /dev/null and b/spec/files/shapefiles/2957_projected.shp differ
diff --git a/spec/files/shapefiles/2957_projected.shx b/spec/files/shapefiles/2957_projected.shx
new file mode 100644
index 0000000000..76fb201959
Binary files /dev/null and b/spec/files/shapefiles/2957_projected.shx differ
diff --git a/spec/files/shapefiles/4267_geographic.dbf b/spec/files/shapefiles/4267_geographic.dbf
new file mode 100644
index 0000000000..fbe08bb88c
Binary files /dev/null and b/spec/files/shapefiles/4267_geographic.dbf differ
diff --git a/spec/files/shapefiles/4267_geographic.prj b/spec/files/shapefiles/4267_geographic.prj
new file mode 100644
index 0000000000..57a74a98fc
--- /dev/null
+++ b/spec/files/shapefiles/4267_geographic.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_North_American_1927",DATUM["D_North_American_1927",SPHEROID["Clarke_1866",6378206.4,294.978698213898]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/spec/files/shapefiles/4267_geographic.shp b/spec/files/shapefiles/4267_geographic.shp
new file mode 100644
index 0000000000..b99930fc4f
Binary files /dev/null and b/spec/files/shapefiles/4267_geographic.shp differ
diff --git a/spec/files/shapefiles/4267_geographic.shx b/spec/files/shapefiles/4267_geographic.shx
new file mode 100644
index 0000000000..6ec8a4e611
Binary files /dev/null and b/spec/files/shapefiles/4267_geographic.shx differ
diff --git a/spec/files/shapefiles/four_valid_shapes.cpg b/spec/files/shapefiles/four_valid_shapes.cpg
new file mode 100644
index 0000000000..3ad133c048
--- /dev/null
+++ b/spec/files/shapefiles/four_valid_shapes.cpg
@@ -0,0 +1 @@
+UTF-8
\ No newline at end of file
diff --git a/spec/files/shapefiles/four_valid_shapes.dbf b/spec/files/shapefiles/four_valid_shapes.dbf
new file mode 100644
index 0000000000..8e86f9f8aa
Binary files /dev/null and b/spec/files/shapefiles/four_valid_shapes.dbf differ
diff --git a/spec/files/shapefiles/four_valid_shapes.prj b/spec/files/shapefiles/four_valid_shapes.prj
new file mode 100644
index 0000000000..f45cbadf00
--- /dev/null
+++ b/spec/files/shapefiles/four_valid_shapes.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/spec/files/shapefiles/four_valid_shapes.qix b/spec/files/shapefiles/four_valid_shapes.qix
new file mode 100644
index 0000000000..89c5025115
Binary files /dev/null and b/spec/files/shapefiles/four_valid_shapes.qix differ
diff --git a/spec/files/shapefiles/four_valid_shapes.shp b/spec/files/shapefiles/four_valid_shapes.shp
new file mode 100644
index 0000000000..e27c7f23d9
Binary files /dev/null and b/spec/files/shapefiles/four_valid_shapes.shp differ
diff --git a/spec/files/shapefiles/four_valid_shapes.shx b/spec/files/shapefiles/four_valid_shapes.shx
new file mode 100644
index 0000000000..4318eea964
Binary files /dev/null and b/spec/files/shapefiles/four_valid_shapes.shx differ
diff --git a/spec/files/shapefiles/having_error_shapes.dbf b/spec/files/shapefiles/having_error_shapes.dbf
new file mode 100644
index 0000000000..324c50c8e5
Binary files /dev/null and b/spec/files/shapefiles/having_error_shapes.dbf differ
diff --git a/spec/files/shapefiles/having_error_shapes.geojson b/spec/files/shapefiles/having_error_shapes.geojson
new file mode 100644
index 0000000000..3e08d6999f
--- /dev/null
+++ b/spec/files/shapefiles/having_error_shapes.geojson
@@ -0,0 +1,18 @@
+{
+"type": "FeatureCollection",
+"comment": "Run `ogr2ogr having_error_shapes.shp having_error_shapes.geojson` to convert this to a shapefile",
+"name": "having_error_shapes",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
+"features": [
+{ "type": "Feature", "properties": { "Name": "Valid 1", "Status": "Existing", "Type": "Forest", "Shape_STAr": 4961926.4741099998, "Shape_STLe": 18084.337855099999, "iso_a2": "zz", "iso_a3": "ZZZ" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0, 0 ], [ 10, 0 ], [ 10, 10 ], [ 0, 10 ], [ 0, 0 ] ] ] ] } },
+
+{ "type": "Feature", "properties": { "Name": "Ecoregions issue polygon from OBJECTID 753 has latitudes below the pseudo-mercator min 85.05", "Status": "Existing", "Type": "Forest", "Shape_STAr": 4961926.4741099998, "Shape_STLe": 18084.337855099999, "iso_a2": "", "iso_a3": "" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -179.998285794999958, -85.130434393999963 ], [ -179.998292045999932, -85.139630900999975 ], [ -179.999016806999947, -85.139599626999939 ], [ -179.999005553999922, -85.130401744999972 ], [ -179.998285794999958, -85.130434393999963 ] ] ] ] } },
+
+{ "type": "Feature", "properties": { "Name": "Invalid bowtie doesn't cross anti-meridian", "Status": "Existing", "Type": "Forest", "Shape_STAr": 4961926.4741099998, "Shape_STLe": 18084.337855099999, "iso_a2": "", "iso_a3": "" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 0, 0 ], [ -10, -10 ], [ -10, 0 ], [ 0, -10 ], [ 0, 0 ] ] ] ] } },
+
+{ "type": "Feature", "properties": { "Name": "Crossing anti-meridian in a way that can be interpreted as self-intersection", "Status": "Existing", "Type": "Forest", "Shape_STAr": 4961926.4741099998, "Shape_STLe": 18084.337855099999, "iso_a2": "", "iso_a3": "" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [-200,-10],[-200,10],[-160,10],[-190,0],[-160,-10],[-200,-10] ] ] ] } },
+
+{ "type": "Feature", "properties": { "Name": "Invalid anti-meridian-crossing bowtie", "Status": "Existing", "Type": "Forest", "Shape_STAr": 4961926.4741099998, "Shape_STLe": 18084.337855099999, "iso_a2": "", "iso_a3": "" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 200, 0 ], [ 170, 10 ], [ 170, 0 ], [ 200, 10 ], [ 200, 0 ] ] ] ] } }
+
+]
+}
diff --git a/spec/files/shapefiles/having_error_shapes.prj b/spec/files/shapefiles/having_error_shapes.prj
new file mode 100644
index 0000000000..f45cbadf00
--- /dev/null
+++ b/spec/files/shapefiles/having_error_shapes.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/spec/files/shapefiles/having_error_shapes.shp b/spec/files/shapefiles/having_error_shapes.shp
new file mode 100644
index 0000000000..953da97d78
Binary files /dev/null and b/spec/files/shapefiles/having_error_shapes.shp differ
diff --git a/spec/files/shapefiles/having_error_shapes.shx b/spec/files/shapefiles/having_error_shapes.shx
new file mode 100644
index 0000000000..0bc1f780c8
Binary files /dev/null and b/spec/files/shapefiles/having_error_shapes.shx differ
diff --git a/spec/files/shapefiles/invalid_encoding.cpg b/spec/files/shapefiles/invalid_encoding.cpg
new file mode 100644
index 0000000000..5e40c08770
--- /dev/null
+++ b/spec/files/shapefiles/invalid_encoding.cpg
@@ -0,0 +1 @@
+asdf
\ No newline at end of file
diff --git a/spec/files/shapefiles/non_utf8_encoded.cpg b/spec/files/shapefiles/non_utf8_encoded.cpg
new file mode 100644
index 0000000000..f2572d1482
--- /dev/null
+++ b/spec/files/shapefiles/non_utf8_encoded.cpg
@@ -0,0 +1 @@
+ISO-8859-7
\ No newline at end of file
diff --git a/spec/files/shapefiles/non_utf8_encoded.dbf b/spec/files/shapefiles/non_utf8_encoded.dbf
new file mode 100644
index 0000000000..39c54e6fb3
Binary files /dev/null and b/spec/files/shapefiles/non_utf8_encoded.dbf differ
diff --git a/spec/files/shapefiles/non_utf8_encoded.prj b/spec/files/shapefiles/non_utf8_encoded.prj
new file mode 100644
index 0000000000..f45cbadf00
--- /dev/null
+++ b/spec/files/shapefiles/non_utf8_encoded.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/spec/files/shapefiles/non_utf8_encoded.shp b/spec/files/shapefiles/non_utf8_encoded.shp
new file mode 100644
index 0000000000..5e8e545450
Binary files /dev/null and b/spec/files/shapefiles/non_utf8_encoded.shp differ
diff --git a/spec/files/shapefiles/non_utf8_encoded.shx b/spec/files/shapefiles/non_utf8_encoded.shx
new file mode 100644
index 0000000000..d28034bb39
Binary files /dev/null and b/spec/files/shapefiles/non_utf8_encoded.shx differ
diff --git a/spec/files/shapefiles/not_wgs84.prj b/spec/files/shapefiles/not_wgs84.prj
new file mode 100644
index 0000000000..db31dbdae7
--- /dev/null
+++ b/spec/files/shapefiles/not_wgs84.prj
@@ -0,0 +1 @@
+PROJCS["NAD_1983_HARN_WISCRS_Dane_County_Feet",GEOGCS["GCS_North_American_1983_HARN",DATUM["D_North_American_1983_HARN",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",811000.0],PARAMETER["False_Northing",480943.886],PARAMETER["Central_Meridian",-89.4222222222222],PARAMETER["Standard_Parallel_1",43.0695160375],PARAMETER["Scale_Factor",1.0000384786],PARAMETER["Latitude_Of_Origin",43.0695160375],UNIT["US survey foot",0.304800609601219]]
\ No newline at end of file
diff --git a/spec/helpers/lib/vendor/rgeo_shapefile_helper_spec.rb b/spec/helpers/lib/vendor/rgeo_shapefile_helper_spec.rb
new file mode 100644
index 0000000000..925948c965
--- /dev/null
+++ b/spec/helpers/lib/vendor/rgeo_shapefile_helper_spec.rb
@@ -0,0 +1,199 @@
+require 'rails_helper'
+
+describe Lib::Vendor::RgeoShapefileHelper, type: :helper do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:cpg_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.cpg'),
+ 'text/plain'
+ ))
+ }
+ let(:shapefile) {
+ # This one is all valid
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ cpg_doc_id: cpg_doc.id,
+ name_field: 'Name'
+ }
+ }
+ let(:project_id) { Current.project_id }
+
+ context '::validate_shape_file' do
+ specify 'does not raise on valid shapefile' do
+ expect{validate_shape_file(shapefile, project_id)}.not_to raise_error
+ end
+
+ specify 'name field must exist' do
+ shapefile[:name_field] = 'ASDF'
+ expect{validate_shape_file(shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /ASDF/)
+ end
+
+ specify 'name field must have type String' do
+ shapefile[:name_field] = 'Shape_STLe' # a numeric field
+ expect{validate_shape_file(shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /Name error/)
+ end
+
+ specify 'all records must have a name' do
+ shapefile[:name_field] = 'FacilityID' # one record missing this name
+ expect{validate_shape_file(shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /has no name/)
+ end
+
+ context 'iso_3166_a2 and iso_3166_a3' do
+ specify 'iso_3166_a3 field must have type String' do
+ shapefile[:iso_a3_field] = 'Shape_STLe' # a numeric field
+ expect{validate_shape_file(shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /Iso_3166_a3 error/)
+ end
+
+ specify 'iso_3166_a2 need not be specified for every record' do
+ # Here it's specified for only one record
+ shapefile[:iso_a2_field] = 'iso_a2'
+ expect{validate_shape_file(shapefile, project_id)}
+ .to_not raise_error
+ end
+ end
+
+ specify 'invalid .cpg encoding is caught' do
+ invalid_cpg = Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/invalid_encoding.cpg'),
+ 'text/plain'
+ ))
+ # mini-hack to avoid more document-loading boilerplate
+ invalid_cpg.update!(
+ document_file_file_name: cpg_doc.document_file_file_name
+ )
+ shapefile[:cpg_doc_id] = invalid_cpg.id
+
+ expect{validate_shape_file(shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /unknown encoding name/)
+ end
+
+ context 'filling in unspecified files' do
+ let!(:min_shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ name_field: 'Name'
+ }
+ }
+ specify 'enough to specify a .shp file if the other docs exist' do
+ [shx_doc, dbf_doc, prj_doc] # instantiate these
+ expect{validate_shape_file(min_shapefile, project_id)}
+ .not_to raise_error
+ end
+
+ specify '.shp document must be specified' do
+ # i.e. we don't infer .shp from any of the others
+ sf = {
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ name_field: 'Name'
+ }
+ shp_doc
+ expect{validate_shape_file(sf, project_id)}
+ .to raise_error(TaxonWorks::Error, /.shp file is required/)
+ end
+
+ specify 'fail if an unspecified doc does not exist' do
+ [shx_doc, dbf_doc]
+ expect{validate_shape_file(min_shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /Failed to find/)
+ end
+
+ specify 'do not fail if a duplicate of a specified file exists' do
+ # We don't warn of duplicates on files users explicitly choose
+ # themselves (even though they could be unknowingly choosing the wrong
+ # one)
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shp'),
+ 'application/x-shapefile'
+ ))
+ [shx_doc, dbf_doc, prj_doc]
+ expect{validate_shape_file(min_shapefile, project_id)}
+ .not_to raise_error
+ end
+
+ specify 'fail if multiple unspecified docs with the same name exist' do
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shx'),
+ 'application/x-shapefile'
+ ))
+ [shx_doc, dbf_doc, prj_doc]
+ expect{validate_shape_file(min_shapefile, project_id)}
+ .to raise_error(TaxonWorks::Error, /More than one/)
+ end
+
+ end
+ end
+
+ context '::fields_from_shapefile' do
+ let(:fields) {
+ ['Name', 'FacilityID', 'Status', 'Type', 'Shape_STAr', 'Shape_STLe',
+ 'iso_a2', 'iso_a3']
+ }
+ specify 'returns column names given a dbf_doc_id' do
+ expect(fields_from_shapefile(nil, dbf_doc.id, project_id))
+ .to contain_exactly(*fields)
+ end
+
+ specify 'returns column names given a shp_doc_id' do
+ dbf_doc
+ expect(fields_from_shapefile(shp_doc.id, nil, project_id))
+ .to contain_exactly(*fields)
+ end
+
+ specify 'raises error if dbf is unspecified and no match exists' do
+ expect{fields_from_shapefile(shp_doc.id, nil, project_id)}
+ .to raise_error(TaxonWorks::Error, /Failed to find/)
+ end
+
+ specify 'raises error if multiple unspecified dbf matches exist' do
+ dbf_doc
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.dbf'),
+ 'application/x-shapefile'
+ ))
+ expect{fields_from_shapefile(shp_doc.id, nil, project_id)}
+ .to raise_error(TaxonWorks::Error, /More than one/)
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/spec/jobs/import_gazetteers_job_spec.rb b/spec/jobs/import_gazetteers_job_spec.rb
new file mode 100644
index 0000000000..6c151837a3
--- /dev/null
+++ b/spec/jobs/import_gazetteers_job_spec.rb
@@ -0,0 +1,528 @@
+require 'rails_helper'
+
+RSpec.describe ImportGazetteersJob, type: :job do
+ let!(:user) { FactoryBot.create(:valid_user) }
+ let!(:project) { FactoryBot.create(:valid_project, name: 'Project 1',
+ created_by_id: user.id, updated_by_id: user.id) }
+ let!(:project2) { FactoryBot.create(:valid_project, name: 'Project 2',
+ created_by_id: user.id, updated_by_id: user.id) }
+ let!(:project3) { FactoryBot.create(:valid_project,
+ created_by_id: user.id, updated_by_id: user.id) }
+
+ specify 'queues job in import_gazetteers' do
+ expect { ImportGazetteersJob.perform_later }
+ .to have_enqueued_job.on_queue(:import_gazetteers)
+ end
+
+ context 'a valid shapefile with valid shapes' do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/four_valid_shapes.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:num_shapefile_records) { 4 }
+ let(:shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ name_field: 'Name',
+ iso_a2_field: 'iso_a2',
+ iso_a3_field: 'iso_a3'
+ }
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: false,
+ citation: nil
+ }
+ }
+ let(:progress_tracker) {
+ GazetteerImport.create!(
+ shapefile: shp_doc.document_file_file_name
+ )
+ }
+ let(:projects) { [project.id] }
+
+ before(:each) {
+ Current.user_id = user.id
+ Current.project_id = project.id
+
+ ImportGazetteersJob.perform_now(
+ shapefile, citation_options, user.id, project.id,
+ progress_tracker, projects
+ )
+ }
+
+ specify 'creates the right number of Gazetteers' do
+ expect(Gazetteer.all.count).to eq(num_shapefile_records)
+ end
+
+ context 'cloning into multiple projects' do
+ let(:projects) { [project.id, project2.id] }
+
+ specify 'creates the right number of Gazetteers' do
+ expect(Gazetteer.where(project: ).count)
+ .to eq(num_shapefile_records)
+
+ expect(Gazetteer.where(project: project2).count)
+ .to eq(num_shapefile_records)
+
+ expect(Gazetteer.where(project: project3).count)
+ .to eq(0)
+ end
+ end
+
+ specify 'creates Gazetteers with the expected names' do
+ expect(Gazetteer.all.map { |g| g.name })
+ .to contain_exactly(
+ 'Walking Iron Wildlife Area', 'Walking Iron County Park',
+ 'Morton Forest', 'Halfway Prairie School'
+ )
+ end
+
+ specify 'citation option off creates GZs with no citations' do
+ expect(Citation.any?).to eq(false)
+ end
+
+ specify 'creates iso a2 values' do
+ expect(Gazetteer.all.order(:id).map { |g| g.iso_3166_a2 })
+ .to eq([nil, nil, 'ZZ', nil])
+ end
+
+ specify 'creates iso a3 values' do
+ expect(Gazetteer.all.order(:id).map { |g| g.iso_3166_a3 })
+ .to eq([nil, nil, 'ZZZ', nil])
+ end
+
+ context 'progress tracking' do
+ specify 'records number of gzs in the shapefile' do
+ expect(progress_tracker.num_records).to eq(num_shapefile_records)
+ end
+
+ specify 'records number of gzs created on completion' do
+ expect(progress_tracker.num_records_imported)
+ .to eq(num_shapefile_records)
+ end
+
+ specify 'has empty `error_messages` on success' do
+ expect(progress_tracker.error_messages).to eq(nil)
+ end
+
+ context 'with an error causing the job to quit' do
+ let(:citation_options) {
+ {
+ cite_gzs: true,
+ citation: {
+ is_original: false,
+ pages: '3-4',
+ source_id: 123456 # doesn't exist
+ }
+ }
+ }
+
+ specify 'records the abort reason' do
+ expect(progress_tracker.error_messages)
+ .to include('Citations source')
+ end
+
+ specify 'records the number of records processed before abort' do
+ expect(progress_tracker.num_records_imported).to eq(0)
+ end
+
+ context 'cloning into multiple projects' do
+ let(:projects) { [project.id, project2.id] }
+
+ specify 'records the projects imported to' do
+ expect(progress_tracker.project_names).to match(/Project 1.+Project 2/)
+ end
+ end
+ end
+ end
+
+ context 'with citation' do
+ let(:source) {
+ FactoryBot.create(:valid_source)
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: true,
+ citation: {
+ is_original: false,
+ pages: '3-4',
+ source_id: source.id
+ }
+ }
+ }
+
+ specify 'each created GZ has the expected citation' do
+ expect(
+ Gazetteer.all.map { |g| g.citations.map { |c| c.source_id } }.flatten
+ ).to eq([source.id] * num_shapefile_records)
+ end
+
+ context 'cloned into multiple projects' do
+ let(:projects) { [project.id, project2.id] }
+
+ specify 'there are the expected number of citations' do
+ expect(Citation.where(project_id: project.id).count)
+ .to eq(num_shapefile_records)
+
+ expect(Citation.where(project_id: project2.id).count)
+ .to eq(num_shapefile_records)
+
+ expect(Citation.where(project_id: project3.id).count)
+ .to eq(0)
+ end
+
+ end
+ end
+
+ end
+
+ context 'a valid shapefile with some invalid shapes' do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/having_error_shapes.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/having_error_shapes.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/having_error_shapes.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/having_error_shapes.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ name_field: 'Name',
+ iso_a2_field: 'iso_a2',
+ iso_a3_field: 'iso_a3'
+ }
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: false,
+ citation: nil
+ }
+ }
+ let(:progress_tracker) {
+ GazetteerImport.create!(
+ shapefile: shp_doc.document_file_file_name
+ )
+ }
+ let(:projects) { [project.id] }
+
+ before(:each) do
+ Current.user_id = user.id
+ Current.project_id = project.id
+
+ ImportGazetteersJob.perform_now(
+ shapefile, citation_options, user.id, project.id,
+ progress_tracker, projects
+ )
+ end
+
+ specify "invalid shapes that don't cross anti-meridian are imported to valid shapes" do
+ expect(
+ Gazetteer.where(name: "Invalid bowtie doesn't cross anti-meridian")
+ .first.geo_object.valid?
+ ).to be true
+ end
+
+ specify 'invalid shapes that cross anti-meridian are imported to valid shapes' do
+ expect(
+ Gazetteer.where(name: 'Invalid anti-meridian-crossing bowtie')
+ .first.geo_object.valid?
+ ).to be true
+ end
+
+ specify "valid shapes that cross anti-meridian are imported to shapes that don't cross the anti-meridian" do
+ s_wkt = Gazetteer.where(name: 'Crossing anti-meridian in a way that can be interpreted as self-intersection')
+ .first.geo_object.as_text
+
+ expect(GeographicItem.crosses_anti_meridian?(s_wkt))
+ .to be false
+ end
+
+ specify "invalid shapes that cross anti-meridian are imported to shapes that don't cross the anti-meridian" do
+ s_wkt = Gazetteer.where(name: 'Invalid anti-meridian-crossing bowtie')
+ .first.geo_object.as_text
+
+ expect(GeographicItem.crosses_anti_meridian?(s_wkt))
+ .to be false
+ end
+ end
+
+ context 'with a .cpg file' do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/non_utf8_encoded.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/non_utf8_encoded.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/non_utf8_encoded.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/non_utf8_encoded.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:cpg_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/non_utf8_encoded.cpg'),
+ 'text/plain'
+ ))
+ }
+ let(:shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ cpg_doc_id: cpg_doc.id,
+ name_field: 'Name',
+ iso_a2_field: 'iso_a2',
+ iso_a3_field: 'iso_a3'
+ }
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: false,
+ citation: nil
+ }
+ }
+ let(:progress_tracker) {
+ GazetteerImport.create!(
+ shapefile: shp_doc.document_file_file_name
+ )
+ }
+ let(:projects) { [project.id] }
+
+ before(:each) do
+ Current.user_id = user.id
+ Current.project_id = project.id
+
+ ImportGazetteersJob.perform_now(
+ shapefile, citation_options, user.id, project.id,
+ progress_tracker, projects
+ )
+ end
+
+ specify 'string attributes are imported correctly' do
+ # Under normal circumstances the dbf gem returns all string attributes
+ # encoded as UTF-8.
+ expect(Gazetteer.first.name.encoding.to_s).to eq('UTF-8')
+
+ expect(Gazetteer.first.name).to eq('ΣπόντζΜπομπ')
+ end
+
+ end
+
+ context 'with a non-WGS84 projected CRS' do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/2957_projected.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/2957_projected.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/2957_projected.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/2957_projected.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ name_field: 'Name',
+ }
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: false,
+ citation: nil
+ }
+ }
+ let(:progress_tracker) {
+ GazetteerImport.create!(
+ shapefile: shp_doc.document_file_file_name
+ )
+ }
+ let(:projects) { [project.id] }
+
+ before(:each) do
+ Current.user_id = user.id
+ Current.project_id = project.id
+
+ ImportGazetteersJob.perform_now(
+ shapefile, citation_options, user.id, project.id,
+ progress_tracker, projects
+ )
+ end
+
+ specify 'imports correct number of shapes' do
+ expect(Gazetteer.all.count).to eq(1)
+ end
+
+ specify 'imports expected shape' do
+ # The imported polygon should cover an area around Regina, Alberta
+ expect(Gazetteer.first.geo_object.contains?(
+ Gis::FACTORY.point(-104.599, 50.421)
+ )).to be true
+ end
+ end
+
+ context 'with a non-WGS84 projected CRS' do
+ let(:shp_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/4267_geographic.shp'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:shx_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/4267_geographic.shx'),
+ 'application/x-shapefile'
+ ))
+ }
+ let(:dbf_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/4267_geographic.dbf'),
+ 'application/x-dbf'
+ ))
+ }
+ let(:prj_doc) {
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ (Rails.root + 'spec/files/shapefiles/4267_geographic.prj'),
+ 'text/plain'
+ ))
+ }
+ let(:shapefile) {
+ {
+ shp_doc_id: shp_doc.id,
+ shx_doc_id: shx_doc.id,
+ dbf_doc_id: dbf_doc.id,
+ prj_doc_id: prj_doc.id,
+ name_field: 'Name',
+ }
+ }
+ let(:citation_options) {
+ {
+ cite_gzs: false,
+ citation: nil
+ }
+ }
+ let(:progress_tracker) {
+ GazetteerImport.create!(
+ shapefile: shp_doc.document_file_file_name
+ )
+ }
+ let(:projects) { [project.id] }
+
+ before(:each) do
+ Current.user_id = user.id
+ Current.project_id = project.id
+
+ ImportGazetteersJob.perform_now(
+ shapefile, citation_options, user.id, project.id,
+ progress_tracker, projects
+ )
+ end
+
+ specify 'imports correct number of shapes' do
+ expect(Gazetteer.all.count).to eq(1)
+ end
+
+ specify 'imports expected shape' do
+ # The imported polygon should cover an area in Alberta
+ expect(Gazetteer.first.geo_object.contains?(
+ Gis::FACTORY.point(-104.215, 52.359)
+ )).to be true
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/spec/lib/batch_load/import/collecting_events/gpx_interpreter_spec.rb b/spec/lib/batch_load/import/collecting_events/gpx_interpreter_spec.rb
index 2ab54f568a..9aa0ce5190 100644
--- a/spec/lib/batch_load/import/collecting_events/gpx_interpreter_spec.rb
+++ b/spec/lib/batch_load/import/collecting_events/gpx_interpreter_spec.rb
@@ -12,7 +12,7 @@
# let(:setup) {
# gpx = GPX::GPXFile.new(file_name)
# }
-
+
let(:import) { BatchLoad::Import::CollectingEvents::GPXInterpreter.new(
project_id: project.id,
user_id: user.id,
@@ -24,8 +24,8 @@
specify 'baseline is 0' do
expect(CollectingEvent.count).to eq(0)
expect(Georeference::GPX.count).to eq(0)
- expect(GeographicItem::Point.count).to eq(0)
- expect(GeographicItem::LineString.count).to eq(0)
+ expect(GeographicItem.points.count).to eq(0)
+ expect(GeographicItem.line_strings.count).to eq(0)
end
specify '.new succeeds' do
@@ -70,11 +70,12 @@
context 'geographic_items' do
specify 'point' do
- expect(GeographicItem::Point.count).to eq(1)
+ expect(GeographicItem.points.count).to eq(1)
end
specify 'line_string' do
- expect(GeographicItem::LineString.count).to eq(1)
+ expect(
+ GeographicItem.line_strings.count).to eq(1)
end
end
end
diff --git a/spec/lib/gis/geo_json_spec.rb b/spec/lib/gis/geo_json_spec.rb
index 84d29b938a..c507608b10 100644
--- a/spec/lib/gis/geo_json_spec.rb
+++ b/spec/lib/gis/geo_json_spec.rb
@@ -44,17 +44,17 @@
let(:polygon) { RSPEC_GEO_FACTORY.polygon(polygon_outer, [polygon_inner]) }
- let(:polygon_b) { FactoryBot.create(:geographic_item_polygon, polygon: polygon.as_binary, by: geo_user) }
+ let(:polygon_b) { FactoryBot.create(:geographic_item, geography: polygon.as_binary, by: geo_user) }
- let(:gi_line_string) { FactoryBot.create(:geographic_item_line_string, line_string: linestring.as_binary) }
+ let(:gi_line_string) { FactoryBot.create(:geographic_item, geography: linestring.as_binary) }
- let(:multipoint_b) { FactoryBot.create(:geographic_item_multi_point, multi_point: multipoint.as_binary) }
+ let(:multipoint_b) { FactoryBot.create(:geographic_item, geography: multipoint.as_binary) }
- let(:multilinestring_b) { FactoryBot.create(:geographic_item_multi_line_string,
- multi_line_string: multilinestring.as_binary) }
+ let(:multilinestring_b) { FactoryBot.create(:geographic_item,
+ geography: multilinestring.as_binary) }
- let(:multipolygon_b) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: multipolygon.as_binary,
+ let(:multipolygon_b) { FactoryBot.create(:geographic_item,
+ geography: multipolygon.as_binary,
by: geo_user) }
context "outputting GeoJSON 'Feature's" do
@@ -117,8 +117,8 @@
expect(json).to eq({ 'type' => 'FeatureCollection',
'features' => [{ 'type' => 'Feature',
'geometry' => { 'type' => 'Polygon',
- 'coordinates' => [[[1, -1, 0], [9, -1, 0],
- [9, -9, 0], [1, -9, 0],
+ 'coordinates' => [[[1, -1, 0], [1, -9, 0],
+ [9, -9, 0], [9, -1, 0],
[1, -1, 0]],
[[2.5, -2.5, 0], [7.5, -2.5, 0],
[7.5, -7.5, 0], [2.5, -7.5, 0],
@@ -156,17 +156,17 @@
expect(json).to eq({ 'type' => 'FeatureCollection',
'features' => [{ 'type' => 'Feature',
'geometry' => { 'type' => 'MultiPolygon',
- 'coordinates' => [[[[1, -1, 0], [9, -1, 0],
- [9, -9, 0], [1, -9, 0], [1, -1, 0]]],
- [[[2.5, -2.5, 0], [7.5, -2.5, 0],
- [7.5, -7.5, 0], [2.5, -7.5, 0],
+ 'coordinates' => [[[[1, -1, 0], [1, -9, 0],
+ [9, -9, 0], [9, -1, 0], [1, -1, 0]]],
+ [[[2.5, -2.5, 0], [2.5, -7.5, 0],
+ [7.5, -7.5, 0], [7.5, -2.5, 0],
[2.5, -2.5, 0]]]] },
'properties' => { 'geographic_item' => { 'id' => multipolygon_b.id } },
'id' => feature_index.to_i }] })
end
specify "that the geographic_item type 'geometry_collection' produce GeoJSON" do
- object = GeographicItem.create!(geometry_collection: 'GEOMETRYCOLLECTION( POLYGON((0.0 0.0 0.0, ' \
+ object = GeographicItem.create!(geography: 'GEOMETRYCOLLECTION( POLYGON((0.0 0.0 0.0, ' \
'10.0 0.0 0.0, 10.0 10.0 0.0, ' \
'0.0 10.0 0.0, 0.0 0.0 0.0)), POINT(10 10 0)) ')
json = Gis::GeoJSON.feature_collection([object])
@@ -193,8 +193,8 @@
expect(json).to eq({ 'type' => 'FeatureCollection',
'features' => [{ 'type' => 'Feature',
'geometry' => { 'type' => 'MultiPolygon',
- 'coordinates' => [[[[0, 0, 0], [0, 10, 0],
- [10, 10, 0], [10, 0, 0],
+ 'coordinates' => [[[[0, 0, 0], [10, 0, 0],
+ [10, 10, 0], [0, 10, 0],
[0, 0, 0]]]] },
'properties' => { 'geographic_area' => { 'id' => object.id,
'tag' => 'A' } },
@@ -247,15 +247,15 @@
expect(json).to eq({ 'type' => 'FeatureCollection',
'features' => [{ 'type' => 'Feature',
'geometry' => { 'type' => 'MultiPolygon',
- 'coordinates' => [[[[0.0, 0.0, 0.0], [10.0, 0.0, 0.0],
- [10.0, -10.0, 0.0], [0.0, -10.0, 0.0],
+ 'coordinates' => [[[[0.0, 0.0, 0.0], [0.0, -10.0, 0.0],
+ [10.0, -10.0, 0.0], [10.0, 0.0, 0.0],
[0.0, 0.0, 0.0]]]] },
'properties' => { 'asserted_distribution' => { 'id' => objects[0].id } },
'id' => (feature_index.to_i + 0) },
{ 'type' => 'Feature',
'geometry' => { 'type' => 'MultiPolygon',
- 'coordinates' => [[[[0.0, 10.0, 0.0], [10.0, 10.0, 0.0],
- [10.0, -10.0, 0.0], [0.0, -10.0, 0.0],
+ 'coordinates' => [[[[0.0, 10.0, 0.0], [0.0, -10.0, 0.0],
+ [10.0, -10.0, 0.0], [10.0, 10.0, 0.0],
[0.0, 10.0, 0.0]]]] },
'properties' => { 'asserted_distribution' => { 'id' => objects[1].id } },
'id' => (feature_index.to_i + 1) }] })
@@ -280,8 +280,8 @@
'properties' => { 'geographic_area' => { 'id' => area_b.id,
'tag' => area_b.name } },
'geometry' => { 'type' => 'MultiPolygon',
- 'coordinates' => [[[[0, 0, 0], [10, 0, 0],
- [10, -10, 0], [0, -10, 0],
+ 'coordinates' => [[[[0, 0, 0], [0, -10, 0],
+ [10, -10, 0], [10, 0, 0],
[0, 0, 0]]]] },
'id' => (feature_index.to_i + 1) },
{ 'type' => 'Feature',
diff --git a/spec/lib/queries/asserted_distribution/filter_spec.rb b/spec/lib/queries/asserted_distribution/filter_spec.rb
index ae1d0078bf..7f41b87598 100644
--- a/spec/lib/queries/asserted_distribution/filter_spec.rb
+++ b/spec/lib/queries/asserted_distribution/filter_spec.rb
@@ -15,17 +15,31 @@
let(:small_geo_area) do
a = FactoryBot.create(:level1_geographic_area)
- a.geographic_items << GeographicItem.create!( polygon: small_polygon)
+ a.geographic_items << GeographicItem.create!( geography: small_polygon)
a
end
let(:big_geo_area) do
b = FactoryBot.create(:level1_geographic_area)
- b.geographic_items << GeographicItem.create!( polygon: big_polygon)
+ b.geographic_items << GeographicItem.create!( geography: big_polygon)
b
end
- specify '#taxon_name_id' do
+ let(:small_gz) {
+ FactoryBot.create(:gazetteer,
+ geographic_item:
+ FactoryBot.create(:geographic_item, geography: small_polygon),
+ name: 'small')
+ }
+
+ let(:large_gz) {
+ FactoryBot.create(:gazetteer,
+ geographic_item:
+ FactoryBot.create(:geographic_item, geography: big_polygon),
+ name: 'large')
+ }
+
+ specify '#taxon_name_id' do
ad1
ad2 # Not this one
o1.update(taxon_name_id: FactoryBot.create(:root_taxon_name).id)
@@ -89,7 +103,7 @@
expect(q.all).to contain_exactly(a, b)
end
- specify '#wkt 1' do
+ specify '#wkt 2' do
a = AssertedDistribution.create!(otu: o1, geographic_area: small_geo_area, source: FactoryBot.create(:valid_source))
b = AssertedDistribution.create!(otu: o1, geographic_area: big_geo_area, source: FactoryBot.create(:valid_source))
@@ -117,6 +131,22 @@
expect(q.all.map(&:id)).to contain_exactly(ad2.id)
end
+ specify '#gazetteer_id small gz' do
+ a = AssertedDistribution.create!(otu: o1, geographic_area: small_geo_area, source: FactoryBot.create(:valid_source))
+ _b = AssertedDistribution.create!(otu: o1, geographic_area: big_geo_area, source: FactoryBot.create(:valid_source))
+
+ q.gazetteer_id = small_gz.id
+ expect(q.all).to contain_exactly(a)
+ end
+
+ specify '#gazetteer_id large gz' do
+ a = AssertedDistribution.create!(otu: o1, geographic_area: small_geo_area, source: FactoryBot.create(:valid_source))
+ b = AssertedDistribution.create!(otu: o1, geographic_area: big_geo_area, source: FactoryBot.create(:valid_source))
+
+ q.gazetteer_id = large_gz.id
+ expect(q.all).to contain_exactly(a, b)
+ end
+
# # Source query integration
# specify '#source_id' do
# FactoryBot.create(:valid_asserted_distribution)
diff --git a/spec/lib/queries/biological_association/filter_spec.rb b/spec/lib/queries/biological_association/filter_spec.rb
index 0e9d34f5da..602e399999 100644
--- a/spec/lib/queries/biological_association/filter_spec.rb
+++ b/spec/lib/queries/biological_association/filter_spec.rb
@@ -131,13 +131,13 @@
# smaller
a = FactoryBot.create(:level1_geographic_area)
s1 = a.geographic_items << GeographicItem.create!(
- polygon: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
+ geography: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
)
# bigger
b = FactoryBot.create(:level1_geographic_area)
s2 = b.geographic_items << GeographicItem.create!(
- polygon: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 10.0, 10.0 )
+ geography: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 10.0, 10.0 )
)
# Use smaller
@@ -158,7 +158,7 @@
specify '#geographic_area_id #geographic_area_mode = true (spatial) against Georeference' do
a = FactoryBot.create(:level1_geographic_area)
s = a.geographic_items << GeographicItem.create!(
- polygon: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
+ geography: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
)
o3.update!(collecting_event: FactoryBot.create(:valid_collecting_event, verbatim_latitude: '7.0', verbatim_longitude: '12.0'))
diff --git a/spec/lib/queries/collecting_event/filter_spec.rb b/spec/lib/queries/collecting_event/filter_spec.rb
index df0cace4ba..2251e77bc7 100644
--- a/spec/lib/queries/collecting_event/filter_spec.rb
+++ b/spec/lib/queries/collecting_event/filter_spec.rb
@@ -136,14 +136,14 @@
let(:point_lat) { '10.0' }
let(:point_long) { '10.0' }
- # let(:factory_polygon) { RSPEC_GEO_FACTORY.polygon(point_lat, point_long) }
- let(:factory_point) { RSPEC_GEO_FACTORY.point(point_lat, point_long) }
- let(:geographic_item) { GeographicItem::Point.create!( point: factory_point ) }
+ # let(:factory_polygon) { RSPEC_GEO_FACTORY.polygon(point_lat, point_long) }
+ let(:factory_point) { RSPEC_GEO_FACTORY.point(point_lat, point_long) }
+ let(:geographic_item) { GeographicItem.create!( geography: factory_point ) }
let!(:point_georeference) {
Georeference::VerbatimData.create!(
collecting_event: ce1,
- geographic_item: geographic_item,
+ geographic_item:
)
}
@@ -155,13 +155,24 @@
'{ "type": "Polygon","coordinates": [[ [5.0, 5.0], [15.0, 5.0], [15.0, 15.0], [5.0, 15.0], [5.0, 5.0] ]] }'
}
+ let(:gi_polygon) {
+ FactoryBot.create(:geographic_item, geography: wkt_polygon)
+ }
+
+ let(:gz_polygon) {
+ FactoryBot.create(:gazetteer,
+ geographic_item: gi_polygon,
+ name: 'gi_polygon'
+ )
+ }
+
specify '#wkt (POINT)' do
query.wkt = wkt_point
expect(query.all.map(&:id)).to contain_exactly(ce1.id)
end
specify '#wkt (POLYGON)' do
- query.wkt = wkt_point
+ query.wkt = wkt_polygon
expect(query.all.map(&:id)).to contain_exactly(ce1.id)
end
@@ -175,5 +186,10 @@
expect(query.all.map(&:id)).to contain_exactly(ce1.id)
end
+ specify '#gazetteer_id' do
+ query.gazetteer_id = gz_polygon.id
+ expect(query.all.map(&:id)).to contain_exactly(ce1.id)
+ end
+
end
end
diff --git a/spec/lib/queries/collection_object/filter_spec.rb b/spec/lib/queries/collection_object/filter_spec.rb
index 2d9fffa4ab..f9d596f086 100644
--- a/spec/lib/queries/collection_object/filter_spec.rb
+++ b/spec/lib/queries/collection_object/filter_spec.rb
@@ -607,11 +607,26 @@
end
specify '#geographic_area_id' do
- ce1.update(geographic_area: FactoryBot.create(:valid_geographic_area))
+ ce1.update!(geographic_area: FactoryBot.create(:valid_geographic_area))
query.base_collecting_event_query.geographic_area_id = [ce1.geographic_area.id]
expect(query.all.pluck(:id)).to contain_exactly(co1.id)
end
+ specify '#gazetteer_id' do
+ gi = FactoryBot.create(:valid_geographic_item)
+ _point_georeference =
+ Georeference::VerbatimData.create!(
+ collecting_event: ce1,
+ geographic_item: gi,
+ )
+
+ gz = FactoryBot.create(:gazetteer,
+ geographic_item: gi, name: 'gz matching ce1 georef')
+
+ query.base_collecting_event_query.gazetteer_id = [gz.id]
+ expect(query.all.pluck(:id)).to contain_exactly(co1.id)
+ end
+
specify '#verbatim_locality (partial)' do
query.base_collecting_event_query.verbatim_locality = 'Out there'
query.base_collecting_event_query.wildcard_attribute = ['verbatim_locality']
@@ -695,7 +710,7 @@
# Merge clauses
context 'merge' do
let(:factory_point) { RSPEC_GEO_FACTORY.point('10.0', '10.0') }
- let(:geographic_item) { GeographicItem::Point.create!( point: factory_point ) }
+ let(:geographic_item) { GeographicItem.create!( geography: factory_point ) }
let!(:point_georeference) {
Georeference::VerbatimData.create!(
diff --git a/spec/lib/queries/document/filter_spec.rb b/spec/lib/queries/document/filter_spec.rb
index 4bb9401167..549e033f10 100644
--- a/spec/lib/queries/document/filter_spec.rb
+++ b/spec/lib/queries/document/filter_spec.rb
@@ -20,28 +20,33 @@
}
let!(:pdf_doc) {
- FactoryBot.create(:valid_document)
+ Document.create!(
+ document_file: Rack::Test::UploadedFile.new(
+ Spec::Support::Utilities::Files.generate_pdf(pages:1),
+ 'application/pdf'
+ )
+ )
}
- specify '#file_extension_group matches extension classes with a single extension' do
- query.file_extension_group = 'pdf'
+ specify '#file_extension_group_name matches extension classes with a single extension' do
+ query.file_extension_group_name = 'pdf'
expect(Document.all.count).to eq(3)
expect(query.all.map(&:id)).to eq([pdf_doc.id])
end
- specify '#file_extension_group matches extension classes with multiple extensions' do
- query.file_extension_group = 'nexus'
+ specify '#file_extension_group_name matches extension classes with multiple extensions' do
+ query.file_extension_group_name = 'nexus'
expect(Document.all.count).to eq(3)
- expect(query.all.order(updated_at: :desc).map(&:id)).to eq([nxs_doc.id, nex_doc.id])
+ expect(query.all.map(&:id)).to contain_exactly(nxs_doc.id, nex_doc.id)
end
- specify '#file_extension_group with no matches returns nothing' do
- query.file_extension_group = 'asdffdsa'
+ specify '#file_extension_group_name with no matches returns nothing' do
+ query.file_extension_group_name = 'asdffdsa'
expect(query.all.count).to eq(0)
end
- specify 'empty #file_extension_group matches all files' do
- query.file_extension_group = ''
+ specify 'empty #file_extension_group_name matches all files' do
+ query.file_extension_group_name = ''
expect(query.all.count).to eq(3)
end
end
\ No newline at end of file
diff --git a/spec/lib/queries/otu/filter_spec.rb b/spec/lib/queries/otu/filter_spec.rb
index 46c4b2b3a0..ac32dc116e 100644
--- a/spec/lib/queries/otu/filter_spec.rb
+++ b/spec/lib/queries/otu/filter_spec.rb
@@ -264,13 +264,13 @@
# smaller
a = FactoryBot.create(:level1_geographic_area)
s1 = a.geographic_items << GeographicItem.create!(
- polygon: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
+ geography: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 5.0, 5.0 )
)
# bigger
b = FactoryBot.create(:level1_geographic_area)
s2 = b.geographic_items << GeographicItem.create!(
- polygon: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 10.0, 10.0 )
+ geography: RspecGeoHelpers.make_polygon( RSPEC_GEO_FACTORY.point(10, 10),0,0, 10.0, 10.0 )
)
# Use smaller
@@ -283,6 +283,36 @@
expect(q.all).to contain_exactly( o1 )
end
+ specify '#gazetteer_id, spatial (Query::AssertedDistribution integration)' do
+ o2
+
+ bigger_polygon = RspecGeoHelpers.make_polygon(
+ RSPEC_GEO_FACTORY.point(10, 10), 0, 0, 10.0, 10.0
+ )
+ bigger_gz =
+ FactoryBot.create(:gazetteer,
+ geographic_item:
+ FactoryBot.create(:geographic_item, geography: bigger_polygon),
+ name: 'large'
+ )
+
+ smaller_ga = FactoryBot.create(:level1_geographic_area)
+ smaller_ga.geographic_items << GeographicItem.create!(
+ geography: RspecGeoHelpers.make_polygon(
+ RSPEC_GEO_FACTORY.point(10, 10), 0, 0, 5.0, 5.0
+ )
+ )
+
+ # Use smaller ga
+ AssertedDistribution.create!(otu: o1, geographic_area: smaller_ga,
+ source: FactoryBot.create(:valid_source))
+
+ # Use bigger gz
+ q.gazetteer_id = bigger_gz.id
+
+ expect(q.all).to contain_exactly( o1 )
+ end
+
specify '#wkt against georeference' do
o1
s = Specimen.create(
diff --git a/spec/models/collecting_event/geo_spec.rb b/spec/models/collecting_event/geo_spec.rb
index 2e9ae13c2b..c3dddc9595 100644
--- a/spec/models/collecting_event/geo_spec.rb
+++ b/spec/models/collecting_event/geo_spec.rb
@@ -57,7 +57,7 @@
end
specify 'with #geographic_area (no shape) set #geographic_name_classification returns :geographic_area' do
- collecting_event.update_column(:geographic_area_id, state.id)
+ collecting_event.update_column(:geographic_area_id, state.id)
expect(collecting_event.geographic_name_classification_method).to eq(:geographic_area)
end
end
@@ -194,7 +194,7 @@
FactoryBot.create(:georeference_verbatim_data,
collecting_event: collecting_event,
- geographic_item: GeographicItem.new(point: area_a.default_geographic_item
+ geographic_item: GeographicItem.new(geography: area_a.default_geographic_item
.centroid))
collecting_event.reload
}
diff --git a/spec/models/gazetteer_import_spec.rb b/spec/models/gazetteer_import_spec.rb
new file mode 100644
index 0000000000..f7bdefa7fd
--- /dev/null
+++ b/spec/models/gazetteer_import_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe GazetteerImport, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/gazetteer_spec.rb b/spec/models/gazetteer_spec.rb
new file mode 100644
index 0000000000..0b0e622c41
--- /dev/null
+++ b/spec/models/gazetteer_spec.rb
@@ -0,0 +1,271 @@
+require 'rails_helper'
+
+RSpec.describe Gazetteer, type: :model, group: [:geo, :shared_geo] do
+ let(:gz) { FactoryBot.build(:valid_gazetteer) }
+
+ context 'creation' do
+ context 'validation' do
+ let!(:gz) { Gazetteer.new}
+
+ specify 'name is required' do
+ gz.valid?
+ expect(gz.errors.include?(:name)).to be_truthy
+ end
+
+ specify 'blank names are invalid' do
+ gz.name = ''
+ gz.valid?
+ expect(gz.errors.include?(:name)).to be_truthy
+ end
+ end
+
+ context 'create from shapes' do
+ let!(:new_gz) { Gazetteer.new(name: 'shapes') }
+ let(:poly1_wkt) {
+ 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'
+ }
+ let(:poly1_gi) {
+ FactoryBot.create(:geographic_item, geography: poly1_wkt)
+ }
+ let(:poly2_wkt) {
+ 'POLYGON((5 5, 15 5, 15 15, 5 15, 5 5))'
+ }
+ let(:poly2_gi) {
+ FactoryBot.create(:geographic_item, geography: poly2_wkt)
+ }
+ let(:poly1_union_poly2_wkt) {
+ 'POLYGON((0 0, 10 0, 10 5, 15 5, 15 15, 5 15, 5 10, 0 10, 0 0))'
+ }
+ let(:poly1_union_poly2) {
+ Gis::FACTORY.parse_wkt(poly1_union_poly2_wkt)
+ }
+ let(:poly1_intersect_poly2_wkt) {
+ 'POLYGON((5 5, 10 5, 10 10, 5 10, 5 5))'
+ }
+ let(:poly1_intersect_poly2) {
+ Gis::FACTORY.parse_wkt(poly1_intersect_poly2_wkt)
+ }
+ let(:p1_wkt) { 'POINT(0 0)' }
+ let(:p2_wkt) { 'POINT(1 1)' }
+ let(:p1) { FactoryBot.create(:geographic_item, geography: p1_wkt) }
+ let(:p2) { FactoryBot.create(:geographic_item, geography: p2_wkt) }
+ let(:ga1) { FactoryBot.create(:valid_geographic_area,
+ geographic_areas_geographic_items_attributes: [{geographic_item: poly1_gi}]
+ )
+ }
+ let(:ga2) { FactoryBot.create(:valid_geographic_area,
+ geographic_areas_geographic_items_attributes: [{geographic_item: poly2_gi}]
+ )
+ }
+ let(:gz1) { FactoryBot.create(:gazetteer,
+ name: 'gz1', geographic_item: poly1_gi
+ )
+ }
+ let(:gz2) { FactoryBot.create(:gazetteer,
+ name: 'gz2', geographic_item: poly2_gi
+ )
+ }
+
+ specify 'create from geojson' do
+ shapes = {
+ geojson: [poly1_gi.to_geo_json_feature, poly2_gi.to_geo_json_feature]
+ }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object)
+ .to eq(poly1_union_poly2)
+ end
+
+ specify 'create from wkt' do
+ shapes = { wkt: [poly1_wkt, poly2_wkt] }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object)
+ .to eq(poly1_union_poly2)
+ end
+
+ specify 'create from points' do
+ p1_union_p2 = Gis::FACTORY.parse_wkt('MULTIPOINT(0 0, 1 1)')
+
+ shapes = {
+ geojson: [p1.to_geo_json_feature, p2.to_geo_json_feature]
+ }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object).to eq(p1_union_p2)
+ end
+
+ specify 'create from GAs' do
+ shapes = { ga_combine: [ga1.id, ga2.id] }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object)
+ .to eq(poly1_union_poly2)
+ end
+
+ specify 'create from GZs' do
+ shapes = { gz_combine: [gz1.id, gz2.id] }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object)
+ .to eq(poly1_union_poly2)
+ end
+
+ specify 'accepts shapes from multiple sources' do
+ # Note several of these shapes are the same, so we're not actually
+ # testing that each factor contributes its shapes.
+ shapes = {
+ geojson: [poly1_gi.to_geo_json_feature],
+ wkt: [poly2_wkt],
+ points: [p1.to_geo_json_feature],
+ ga_combine: [ga1.id],
+ gz_combine: [gz2.id]
+ }
+
+ union = Gis::FACTORY.parse_wkt(
+ "GEOMETRYCOLLECTION(#{poly1_union_poly2_wkt}, #{p1_wkt})"
+ )
+
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object).to eq(union)
+ end
+
+ specify 'supports intersection instead of union' do
+ shapes = {
+ geojson: [poly1_gi.to_geo_json_feature, poly2_gi.to_geo_json_feature]
+ }
+ new_gz.build_gi_from_shapes(shapes, false)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object)
+ .to eq(poly1_intersect_poly2)
+ end
+
+ specify 'supports geojson circles' do
+ shapes = { geojson: [
+ '{"type":"Feature","properties":{"radius":10},"geometry":{"type":"Point","coordinates":[0,0]}}'
+ ]
+ }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object).to eq(
+ GeographicItem.circle(Gis::FACTORY.point(0, 0), 10)
+ )
+ end
+
+ specify 'catches invalid WKT' do
+ shapes = { wkt: ['POINT(0 0)', 'asdf'] }
+ new_gz.build_gi_from_shapes(shapes)
+ expect(new_gz.errors.first.type.message).to start_with('Invalid WKT')
+ end
+
+ context 'normalizes longitude of single points' do
+ specify 'for geojson input' do
+ shapes = { geojson: [
+ '{"type":"Feature","properties":{"radius":null},"geometry":{"type":"Point","coordinates":["666","5"]}}'
+ ]
+ }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object).to eq(
+ Gis::FACTORY.point(-54, 5)
+ )
+ end
+
+ specify 'for wkt input' do
+ shapes = { wkt: ['POINT (-190 10)'] }
+ new_gz.build_gi_from_shapes(shapes)
+ new_gz.save!
+ expect(new_gz.geographic_item.geo_object).to eq(
+ Gis::FACTORY.point(170, 10)
+ )
+ end
+ end
+
+ context "produces shapes that don't cross the anti-meridian" do
+ specify 'wkt' do
+ shapes = { wkt: [
+ 'POLYGON (
+ (160.0 -10.0 0.0, 160.0 10.0 0.0, 200.0 10.0 0.0,
+ 200.0 -10.0 0.0, 160.0 -10.0 0.0)
+ )'
+ ]
+ }
+ new_gz.build_gi_from_shapes(shapes)
+
+ expect(
+ GeographicItem.crosses_anti_meridian?(shapes[:wkt].first)
+ ).to be true
+
+ expect(
+ GeographicItem.crosses_anti_meridian?(
+ new_gz.geographic_item.geo_object.as_text
+ )
+ ).to be false
+ end
+
+ specify 'geojson' do
+ shapes = { geojson: [
+ '{"type":"Feature","properties":{}, "geometry":{"type":"Polygon",
+ "coordinates":
+ [[[-200,-10],[-200,10],[-160,10],[-160,-10],[-200,-10]]]
+ }}'
+ ]}
+ new_gz.build_gi_from_shapes(shapes)
+
+ expect(
+ GeographicItem.crosses_anti_meridian?(
+ RGeo::GeoJSON.decode(shapes[:geojson].first).geometry.as_text
+ )
+ ).to be true
+
+ expect(
+ GeographicItem.crosses_anti_meridian?(
+ new_gz.geographic_item.geo_object.as_text
+ )
+ ).to be false
+ end
+ end
+ end
+
+ context 'cloning into multiple projects on creation' do
+ context '#save_and_clone_to_projects' do
+ let(:project2) { FactoryBot.create(:valid_project) }
+ let(:project3) { FactoryBot.create(:valid_project) }
+ let(:p) { Gis::FACTORY.point(1, 2) }
+ let(:g) {
+ a = Gazetteer.new(name: 'a', iso_3166_a2: 'zz')
+ a.build_geographic_item(geography: p)
+ a
+ }
+
+ specify 'saves to expected projects' do
+ Gazetteer.save_and_clone_to_projects(
+ g, [Current.project_id, project2.id]
+ )
+
+ expect(Gazetteer.where(project_id: Current.project_id).count)
+ .to equal(1)
+
+ expect(Gazetteer.where(project_id: project2.id).count)
+ .to equal(1)
+
+ expect(Gazetteer.where(project_id: project3.id).count)
+ .to equal(0)
+ end
+
+ specify 'clones have the expected data' do
+ Gazetteer.save_and_clone_to_projects(
+ g, [Current.project_id, project2.id]
+ )
+ gz2 = Gazetteer.where(project_id: project2.id).first
+
+ expect(gz2.name).to eq('a')
+ expect(gz2.iso_3166_a2).to eq('ZZ')
+ expect(gz2.iso_3166_a3).to eq(nil)
+ expect(gz2.geo_object).to eq(p)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/geographic_area_spec.rb b/spec/models/geographic_area_spec.rb
index 00abdbfc70..08f851b57c 100644
--- a/spec/models/geographic_area_spec.rb
+++ b/spec/models/geographic_area_spec.rb
@@ -3,7 +3,7 @@
# TODO: Where/how to generate the real GeoJSON (RGeo::GeoJSON.encode(object) does not seem to work properly)
-describe GeographicArea, type: :model, group: [:geo, :shared_goe] do
+describe GeographicArea, type: :model, group: [:geo, :shared_geo] do
include_context 'stuff for complex geo tests'
let(:geographic_area) {
FactoryBot.build(:geographic_area_stack)
@@ -316,7 +316,7 @@
let(:geographic_area) { champaign }
let(:gi) {
geographic_area.geographic_areas_geographic_items
- geo_item = GeographicItem::Polygon.create!(polygon: RSPEC_GEO_FACTORY.polygon(list_k))
+ geo_item = GeographicItem.create!(geography: RSPEC_GEO_FACTORY.polygon(list_k))
geographic_area.geographic_areas_geographic_items.first.destroy!
geographic_area.geographic_areas_geographic_items << GeographicAreasGeographicItem.new(geographic_item: geo_item,
data_origin: 'SFG')
@@ -455,7 +455,7 @@
specify 'retrieving geolocate UI parameters as a string' do
# pending 'completion of a method for geolocate_ui_params_string'
- ford.geographic_items << GeographicItem::MultiPolygon.create!(multi_polygon: fc_shape)
+ ford.geographic_items << GeographicItem.create!(geography: fc_shape)
point = ford.geolocate_attributes.slice("Latitude", "Longitude").values.join('|')
expect(ford.geolocate_ui_params_string)
.to eq('http://www.geo-locate.org/web/webgeoreflight.aspx' \
diff --git a/spec/models/geographic_item/anti_meridian_spec.rb b/spec/models/geographic_item/anti_meridian_spec.rb
index 8f39ed909b..9e93403037 100644
--- a/spec/models/geographic_item/anti_meridian_spec.rb
+++ b/spec/models/geographic_item/anti_meridian_spec.rb
@@ -1,10 +1,4 @@
require 'rails_helper'
-# require_relative '../../support/shared_contexts/geo/build_rspec_geo'
-
-# include the subclasses, perhaps move this out
-Dir[Rails.root.to_s + '/app/models/geographic_item/**/*.rb'].each { |file| require_dependency file }
-
-
# An exercise in exploring and handling data that crosses the anti-meridian.
#
# anti-meridian
@@ -20,10 +14,48 @@
#
#
describe GeographicItem, type: :model, group: :geo do
-
- # after(:all) { clean_slate_geo }
-
- let(:shift_method) { PSQL_VERSION >= 2.2 ? 'ST_ShiftLongitude' : 'ST_Shift_Longitude' }
+ context 'Gis::FACTORY longitude conventions' do
+ # See spec/config/initializers/gis_spec.rb for specs on the following
+ # Gis::FACTORY behavior which informs our understanding of RGeo's
+ # (undocumented) anti-meridian handling.
+ #
+ # For any shape with edges (i.e. anything other than Point and MultiPoint),
+ # longitude values in the range [180, 360] are always converted to the range
+ # [-180, 0] by our Gis::FACTORY, so that all longitudes of all non-Point
+ # shapes are in the range [-180, 180].
+ #
+ # Geometrically the interpretation is that the line between any two points
+ # with longitudes in the range [-180, 180] never crosses the anti-meridian
+ # (the -180/180 barrier of that longitude domain), i.e. the line segment
+ # between any two points in our Gis::FACTORY representation always crosses
+ # the meridian if the two points are in different hemispheres, even if
+ # that's the long way between the two points.
+ #
+ # Applying ST_Shift_Longitude converts longitudes in the range [-180, 0] to
+ # the range [180, 360], so converts longitudes in the range [-180, 180] to
+ # longitudes in the range [0, 360]. In this case lines between points never
+ # cross the meridian.
+ #
+ # If you want the shapes your input points describe to cross the meridian -
+ # ACCORDING TO valid?, ST_Is_Valid, make_valid, and ST_MakeValid (others??)
+ # - then leave them in the coordinate representation applied by
+ # Gis::FACTORY. If you instead expect a shape to cross the anti-meridian
+ # then apply ST_ShiftLongitude to the shape.
+ #
+ # How can you tell which was intended for an input shape? After your input
+ # shape has been normalized by the factory, **you can't**.
+ # `intersects_anti_meridian?` (which relies on ST_Intersects) operates on
+ # the assumption that the lines between points of your shape are
+ # shortest-distance geodesics (i.e. ignores the conventions of ST_Is_Valid
+ # etc. described above). That's most likely the interpretation of your shape
+ # that was intended, but it means that (at least for the purposes of
+ # intersection) !!there's no such thing as an edge spanning more than 180
+ # degrees longitude - if that's what you want then you need to break up your
+ # edge into shorter segements!!. Watch for this when importing shapes
+ # you didn't create.
+ #
+ # See specs below for evidence supporting these claims.
+ end
context 'anti-meridian' do
# Containers left side/object/A component of ST_Contains(A, B)
@@ -40,20 +72,20 @@
# test/target/found objects right side/object B component of ST_Contains(A, B)
# points( outside, inside )
- #- antimeridian crossing line contained by anti_box, crosses anti from eastern to western (easterly)
+ #- antimeridian crossing line partially contained by anti_box, crosses anti from eastern to western (easterly)
let(:left_right_anti_line_partial) { 'LINESTRING (170.5 26.0, -179.8 25.5)' } #- partially contained by anti_box
- #- antimeridian crossing line contained by anti_box, crosses anti from western to eastern (westerly)
+ #- antimeridian crossing line partially contained by anti_box, crosses anti from western to eastern (westerly)
let(:right_left_anti_line_partial) { 'LINESTRING (-170.5 26.0, 179.8 25.5)' } #- partially contained by anti_box
# test/target/found objects right side/object B component of ST_Contains(A, B)
# points( outside, outside )
- #- antimeridian crossing line NOT contained by anti_box, crosses anti from eastern to western (easterly)
+ #- line NOT contained by anti_box
let(:left_right_anti_line_out) { 'LINESTRING (170.5 26.0, 175.0 25.5)' }
- #- antimeridian crossing line NOT contained by anti_box, crosses anti from western to eastern (westerly)
+ #- line NOT contained by anti_box
let(:right_left_anti_line_out) { 'LINESTRING (-170.5 26.0, -175.8 25.5)' }
#- antimeridian string
- let(:anti_s) { 'LINESTRING (180 89.0, 180 -89)' }
+ let(:anti_s) { 'LINESTRING (180 89, 180 -89)' }
context 'raw SQL' do
@@ -163,7 +195,7 @@
['-90 26', '0 26', '90 26'].each do |p| # points in really wide box
specify "shifted #{b}/#{p}" do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{send(b)}')), " \
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{send(b)}')), " \
"ST_GeomFromText('POINT(#{p})')) as r;"
).first.r).to be false
end
@@ -172,7 +204,7 @@
['180 26', '179.9 26'].each do |p| # points not in really wide box
specify "shifted #{b}/#{p}" do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{send(b)}')), " \
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{send(b)}')), " \
"ST_GeomFromText('POINT(#{p})')) as r;"
).first.r).to be true
end
@@ -180,15 +212,15 @@
specify "#{b} (positive shifted does not contain negative point)" do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{send(b)}')), " \
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{send(b)}')), " \
"ST_GeomFromText('POINT(-179.9 26)')) as r;"
).first.r).to be false
end
specify "#{b} (both shifted does contain point)" do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{send(b)}')), " \
- "#{shift_method}(ST_GeomFromText('POINT(-179.9 26)'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{send(b)}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('POINT(-179.9 26)'))) as r;"
).first.r).to be true
end
end
@@ -199,15 +231,15 @@
context 'entirely enclosed in right-left anti-box' do
specify 'left-right anti line' do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{right_left_anti_box}')), " \
- "#{shift_method}(ST_GeomFromText('#{left_right_anti_line}'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{right_left_anti_box}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('#{left_right_anti_line}'))) as r;"
).first.r).to be true
end
specify 'west-east line' do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{right_left_anti_box}')), " \
- "#{shift_method}(ST_GeomFromText('#{right_left_anti_line}'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{right_left_anti_box}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('#{right_left_anti_line}'))) as r;"
).first.r).to be true
end
end
@@ -215,15 +247,15 @@
context 'entirely enclosed in left-right anti-box' do
specify 'left-right anti line' do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{left_right_anti_box}')), " \
- "#{shift_method}(ST_GeomFromText('#{left_right_anti_line}'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{left_right_anti_box}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('#{left_right_anti_line}'))) as r;"
).first.r).to be true
end
specify 'right-left anti line' do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{left_right_anti_box}')), " \
- "#{shift_method}(ST_GeomFromText('#{right_left_anti_line}'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{left_right_anti_box}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('#{right_left_anti_line}'))) as r;"
).first.r).to be true
end
end
@@ -235,8 +267,8 @@
@out.each do |s|
specify "#{s}" do
expect(GeographicItem.find_by_sql(
- "SELECT ST_Contains(#{shift_method}(ST_GeomFromText('#{right_left_anti_box}')), " \
- "#{shift_method}(ST_GeomFromText('#{send(s)}'))) as r;"
+ "SELECT ST_Contains(ST_ShiftLongitude(ST_GeomFromText('#{right_left_anti_box}')), " \
+ "ST_ShiftLongitude(ST_GeomFromText('#{send(s)}'))) as r;"
).first.r).to be false
end
end
@@ -246,8 +278,6 @@
end
context 'Demonstrate that anti_boxes are small for geographies/ST_Covers' do
- # Note _lines can not be used in ST_Covers!
-
%I{left_right_anti_box right_left_anti_box}.each do |b|
['-90 26', '0 26', '90 26'].each do |p| # points in really wide box
specify "#{b}/#{p}" do
@@ -277,20 +307,20 @@
# boxes
let(:eastern_box_text) { 'POLYGON(( 176.0 27.0, 179.0 27.0, 179.0 25.0, 176.0 25.0, 176.0 27.0))' }
- let(:eastern_box) { GeographicItem.create(polygon: Gis::FACTORY.parse_wkt(eastern_box_text)) }
+ let(:eastern_box) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt(eastern_box_text)) }
let(:western_box_text) { 'POLYGON((-179.0 27.0, -176.0 27.0, -176.0 25.0, -179.0 25.0, -179.0 27.0))' }
- let(:western_box) { GeographicItem.create(polygon: Gis::FACTORY.parse_wkt(western_box_text)) }
- let(:crossing_box) { GeographicItem.create(polygon: Gis::FACTORY.parse_wkt(left_right_anti_box)) }
+ let(:western_box) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt(western_box_text)) }
+ let(:crossing_box) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt(left_right_anti_box)) }
# lines
- let(:l_r_line) { GeographicItem.create(line_string: Gis::FACTORY.parse_wkt(left_right_anti_line)) }
- let(:r_l_line) { GeographicItem.create(line_string: Gis::FACTORY.parse_wkt(right_left_anti_line)) }
+ let(:l_r_line) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt(left_right_anti_line)) }
+ let(:r_l_line) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt(right_left_anti_line)) }
# points
- let(:point_in_eastern_box) { GeographicItem.create(point: Gis::FACTORY.parse_wkt('POINT(177 26.0)')) }
- let(:point_in_europe) { GeographicItem.create(point: Gis::FACTORY.parse_wkt('POINT(17 26.0)')) }
- let(:point_in_england) { GeographicItem.create(point: Gis::FACTORY.parse_wkt('POINT(1 26.0)')) }
- let(:point_in_western_box) { GeographicItem.create(point: Gis::FACTORY.parse_wkt('POINT(-177.0 26.0)')) }
+ let(:point_in_eastern_box) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt('POINT(177 26.0)')) }
+ let(:point_in_europe) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt('POINT(17 26.0)')) }
+ let(:point_in_england) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt('POINT(1 26.0)')) }
+ let(:point_in_western_box) { GeographicItem.create(geography: Gis::FACTORY.parse_wkt('POINT(-177.0 26.0)')) }
let(:build_structure) { [western_box, eastern_box, point_in_western_box,
point_in_eastern_box, point_in_europe, point_in_england,
@@ -332,48 +362,13 @@
end
end
- context '.contained_by_with_antimeridian_check(*ids)' do
+ context 'shifted wkt' do
before { build_structure }
- specify 'results from single non-meridian crossing polygon is found' do
- # invokes geometry_sql2
- # using contained_by_with_antimeridian_check is not harmful for non-crossing objects
- expect(GeographicItem.contained_by_with_antimeridian_check(western_box.id).map(&:id))
- .to contain_exactly(point_in_western_box.id, western_box.id)
- end
-
- specify 'results from multiple non-meridian crossing polygons are found' do
- # invokes geometry_sql2
- # using contained_by_with_antimeridian_check is not harmful for non-crossing objects
- expect(GeographicItem.contained_by_with_antimeridian_check(eastern_box.id, western_box.id).map(&:id))
- .to contain_exactly(point_in_eastern_box.id,
- point_in_western_box.id,
- eastern_box.id,
- western_box.id)
- end
-
- specify 'results from single meridian crossing polygon are found' do
- # why is crossing_box not finding l_r_line or r_l_line
- # why does crossing_box find point_in_eastern_box
- expect(GeographicItem.contained_by_with_antimeridian_check(crossing_box.id).map(&:id))
- .to contain_exactly(l_r_line.id, r_l_line.id, crossing_box.id)
- end
-
- specify 'results from merdian crossing and non-meridian crossing polygons are found' do
- # why is crossing_box not finding l_r_line or r_l_line
- expect(GeographicItem.contained_by_with_antimeridian_check(eastern_box.id, western_box.id, crossing_box.id)
- .map(&:id)).to contain_exactly(point_in_eastern_box.id,
- point_in_western_box.id,
- l_r_line.id, r_l_line.id,
- eastern_box.id,
- western_box.id,
- crossing_box.id)
- end
-
specify 'shifting an already shifted polygon has no effect' do
shifted_wkt = eastern_box.geo_object.to_s
expect(shifted_wkt =~ /-/).to be_falsey
- expect(GeographicItem.where(GeographicItem.contained_by_wkt_sql(shifted_wkt)).map(&:id))
+ expect(GeographicItem.where(GeographicItem.covered_by_wkt_sql(shifted_wkt)).map(&:id))
.to contain_exactly(point_in_eastern_box.id, eastern_box.id)
end
end
@@ -382,13 +377,306 @@
context 'verify GeographicItem.crosses_anti_meridian?(wkt) works' do
let(:eastern_box_text) { 'POLYGON(( 176.0 27.0, 179.0 27.0, 179.0 25.0, 176.0 25.0, 176.0 27.0))' }
- specify 'left_right_anti_box' do
- expect(GeographicItem.crosses_anti_meridian?(left_right_anti_box)).to be_truthy
+ %I{left_right_anti_box right_left_anti_box
+ left_right_anti_line right_left_anti_line}.each do |p|
+ specify "#{p}" do
+ expect(GeographicItem.crosses_anti_meridian?("#{send(p)}")).to be_truthy
+ end
end
specify 'eastern_box_text' do
expect(GeographicItem.crosses_anti_meridian?(eastern_box_text)).to be_falsey
end
+
+ context 'anti-meridian crossing shapes from leaflet decoded with Gis::FACTORY' do
+ # See the xspecify example below for context
+ [
+ # drawn on leaflet clockwise
+ 'POLYGON (
+ (160.0 -10.0 0.0, 160.0 10.0 0.0, -160.0 10.0 0.0,
+ 170.0 0.0 0.0, -160.0 -10.0 0.0, 160.0 -10.0 0.0)
+ )',
+ # drawn on leaflet counter-clockwise
+ 'POLYGON (
+ (-160.0 10.0 0.0, 160.0 10.0 0.0, 160.0 -10.0 0.0,
+ -160.0 -10.0 0.0, 170.0 0.0 0.0, -160.0 10.0 0.0)
+ )',
+ ].each do |p|
+ specify "#{p}" do
+ expect(GeographicItem.crosses_anti_meridian?("#{p}")).to be_truthy
+ end
+ end
+ end
+
+ context 'intersects assumes edges are shortest-length geodesics' do
+ specify 'no such thing as an edge spanning more than 180 degrees longitude' do
+ expect(GeographicItem.crosses_anti_meridian?(
+ # For the purposes of ST_Is_Valid, this edge in a polygon would be
+ # interpreted as crossing the meridian (since its coordinates are
+ # in [-180, 180]). Here with ST_Intersection it's the
+ # shortest-distance edge crossing the anti-meridian.
+ 'LINESTRING (-170 0, 20 0)'
+ )
+ ).to be_truthy
+ end
+
+ xspecify 'edges with a vertex on the meridian and spanning more than 90 degrees (but < 180) may intersect the anti-meridian' do
+ # TODO Why does this happen? What's the logic behind it? The behavior
+ # only occurs when one of the endpoints has longitude 0 and seems to
+ # require a longitude span of > 90; other factors are undetermined.
+ expect(GeographicItem.crosses_anti_meridian?(
+ 'LINESTRING (91 10, 0 0)'
+ )
+ ).to be_falsey # as expected
+
+ expect(GeographicItem.crosses_anti_meridian?(
+ 'LINESTRING (91 0, 0 10)'
+ )
+ ).to be_truthy # why???
+ end
+ end
+ end
+
+ context 'Leaflet/GeoJSON inputs and Gis::FACTORY considerations' do
+ let(:p) {
+ # *
+ # --------*--
+ # | * /
+ # | */
+ # | /
+ # | /*
+ # | / *
+ # | \ *
+ # | \*
+ # | \
+ # | *\
+ # | * \
+ # --------*--
+ # *
+ # p is the GeoJson feature returned by leaflet for the shape drawn on
+ # leaflet as above, where * is the anti-meridian.
+ '{"type":"Feature","properties":{},
+ "geometry":{"type":"Polygon",
+ "coordinates":
+ [[[-200,-10],[-200,10],[-160,10],[-190,0],[-160,-10],[-200,-10]]]
+ }}'
+ }
+ let(:s) {
+ # The geo_factory argument is significant here for the way it normalizes
+ # coordinates - see longitude specs above.
+ RGeo::GeoJSON.decode(p, geo_factory: Gis::FACTORY)
+ # s.geometry.as_text = POLYGON (
+ # (160.0 -10.0 0.0, 160.0 10.0 0.0, -160.0 10.0 0.0, 170.0 0.0 0.0,
+ # -160.0 -10.0 0.0, 160.0 -10.0 0.0)
+ # )
+ }
+
+ xspecify 'valid leaflet-provided anti-meridian-crossing shapes can become
+ invalid when normalized by Gis::FACTORY' do
+ # s as normalized by our factory is invalid according to both rgeo and
+ # postgis:
+ expect(s.geometry.valid?).to be false
+ expect(GeographicItem.find_by_sql(
+ "SELECT ST_IsValid(ST_GeomFromText('#{s.geometry.as_text}')) as r;"
+ ).first.r).to be false
+ # ... due to a self-intersection
+ expect(s.geometry.invalid_reason).to eq('Self-intersection')
+ # The postgis invalid reason is 'Self-intersection at or near point 160
+ # 0.303030303030303 0' which is consistent with the segment from
+ # (-160,0) to (170,0) being interpreted as crossing the meridian (going
+ # the "long" way) and not the anti-meridian.
+
+ # By comparison if we decode using the default factory which doesn't
+ # normalize longitudes:
+ s2 = RGeo::GeoJSON.decode(p)
+ # then we get
+ # s2.geometry.as_text = POLYGON ((-200 -10, -200 10, -160 10, -190 0, -160 -10, -200 -10)) # same as the geojson feature coordinates
+ # which **is** valid but cannot be stored in our factory in this form
+ # given our factory's normalization conventions - this shape would have
+ # to be split in two across the anti-meridian to be stored in our
+ # Gis::FACTORY.
+ expect(s2.geometry.valid?).to be true
+ expect(GeographicItem.find_by_sql(
+ "SELECT ST_IsValid(ST_GeomFromText('#{s2.geometry.as_text}')) as r;"
+ ).first.r).to be true
+
+ # In spite of what both rgeo and postgis suggest about s crossing the
+ # meridian instead of the anti-meridian, both s and s2 are reported to
+ # intersect the anti_meridian by postgis. (A similar rgeo intersection
+ # check on s fails because s has a self-intersection.)
+ expect(GeographicItem.crosses_anti_meridian?(s.geometry.as_text)).to be true
+ expect(GeographicItem.crosses_anti_meridian?(s2.geometry.as_text)).to be true
+ end
+
+ context 'st_shifting an anti-meridian-crossing shape normalized by our gis factory produces a valid shape' do
+ # Can't test rgeo-valid here without bypassing our GIS::Factory, which
+ # we don't want to do.
+ specify 'st_shifted(s) is postgis valid' do
+ expect(ActiveRecord::Base.connection.select_value('SELECT ' +
+ GeographicItem.st_is_valid_sql(
+ GeographicItem.st_shift_longitude_sql(
+ GeographicItem.st_geom_from_text_sql(
+ s.geometry.as_text
+ )
+ )
+ ).to_sql
+ )).to be true
+ # The shifted shape here is
+ # POLYGON Z ((160 -10 0,160 10 0,200 10 0,170 0 0,200 -10 0,160 -10 0))
+ end
+ end
+
+ context 'GeographicItem.split_along_anti_meridian(wkt)' do
+ # GeographicItem.split_along_anti_meridian has the pre-condition that
+ # its input crosses_anti_meridian? and so applies ST_ShiftLongitude to
+ # match that interpretation of the input.
+ context 'Leaflet-provided GIS:Factory polygon' do
+ let!(:wkt) {
+ # Make sure it's represented as it would be internally:
+ Gis::FACTORY.parse_wkt(
+ 'POLYGON (
+ (160.0 -10.0 0.0, 160.0 10.0 0.0, -160.0 10.0 0.0,
+ 170.0 0.0 0.0, -160.0 -10.0 0.0, 160.0 -10.0 0.0)
+ )'
+ ).as_text
+ }
+
+ specify 'splits into 3 pieces' do
+ mp = GeographicItem.split_along_anti_meridian(wkt)
+ expect(mp.geometry_type.to_s).to eq('MultiPolygon')
+ expect(mp.num_geometries).to eq(3)
+ end
+
+ specify 'split pieces are valid' do
+ mp = GeographicItem.split_along_anti_meridian(wkt)
+ mp.each do |p|
+ expect(p.valid?).to be true
+ end
+ end
+
+ specify "results don't cross the anti-meridian" do
+ mp = GeographicItem.split_along_anti_meridian(wkt)
+ mp.each do |p|
+ expect(GeographicItem.crosses_anti_meridian?(p.as_text)).to be false
+ end
+ expect(GeographicItem.crosses_anti_meridian?(mp.as_text)).to be false
+ end
+
+ specify 'split pieces include expected points' do
+ mp = GeographicItem.split_along_anti_meridian(wkt)
+ # the left piece
+ expect(mp[0].contains?(Gis::FACTORY.point(165, 0))).to be true
+ # the lower right piece
+ expect(mp[1].contains?(Gis::FACTORY.point(-175, -5))).to be true
+ # the upper right piece
+ expect(mp[2].contains?(Gis::FACTORY.point(-175, 5))).to be true
+ end
+ end
+ end
+
+ context 'Attempting to make arbitrary input shapes both valid and not anti-meridian-crossing' do
+ let(:devils_bowtie) {
+ # Make sure it's represented as it would be internally:
+ Gis::FACTORY.parse_wkt(
+ 'POLYGON ((200 0, 170 10, 170 0, 200 10, 200 0))'
+ ).as_text
+ }
+
+ # If you try to make_valid first, you get the "wrong" result on shapes
+ # that are anti-meridian-crossing:
+ xspecify 'make_valid on anti-meridian-crossing shapes - as normalized by our factory - gives undesired results' do
+ # This makes the **meridian-crossing interpretation** of the shape
+ # valid:
+ new_s = s.make_valid
+ expect(new_s.contains?(Gis::FACTORY.point(0, 9))).to be true
+ end
+
+ # The correct action here would be to make_valid on the shifted shape,
+ # but we shouldn't shift unless we know crosses_anti_meridian?, so we'd
+ # like crosses_anti_meridian? to tolerate (i.e. not raise and give the
+ # correct result on) invalid shapes, both crossing and not crossing the
+ # anti-meridian.
+ specify 'crosses_anti_meridian? tolerates non-anti-meridian-crossing invalid shape' do
+ inv_s = 'POLYGON ((0 0, 10 10, 10 0, 0 10, 0 0))' # bowtie
+ expect(GeographicItem.crosses_anti_meridian?(inv_s)).to be false
+ end
+
+ specify 'crosses_anti_meridian? tolerates anti-meridian-crossing invalid shape' do
+ expect(GeographicItem.crosses_anti_meridian?(devils_bowtie)).to be true
+ end
+
+ # Using ST_Intersects on an invalid shape can blow up
+ xspecify 'failure on split_along_anti_meridian for invalid anti-meridian-crossing shape' do
+ expect{GeographicItem.split_along_anti_meridian(devils_bowtie)}
+ .to raise_error ActiveRecord::StatementInvalid, /geom_intersection/
+ end
+
+ specify 'make_valid(shifted(anti_meridian-crossing-shape)) gives the expected result' do
+ # This is tricky because make_valid operates correctly on a shifted
+ # shape, but when it gets saved back to our Gis::FACTORY its
+ # coordinates get saved as meridian-crossing/non-shifted, which is
+ # "wrong" - it's still crosses_anti_meridian? true - but we can't
+ # store it that way in our factory.
+
+ new_s = Gis::FACTORY.parse_wkb(
+ ActiveRecord::Base.connection.select_value('SELECT ' +
+ GeographicItem.anti_meridian_crossing_make_valid_sql(
+ devils_bowtie
+ ).to_sql
+ )
+ )
+
+ expect(new_s.as_text).to eq('MULTIPOLYGON (((170.0 10.0 0.0, -175.0 5.0 0.0, 170.0 0.0 0.0, 170.0 10.0 0.0)), ((-160.0 10.0 0.0, -160.0 0.0 0.0, -175.0 5.0 0.0, -160.0 10.0 0.0)))')
+
+ expect(GeographicItem.crosses_anti_meridian?(new_s.as_text)).to be true
+ end
+
+ # To complete the process, now that we've made the
+ # anti-meridian-crossing invalid shape into its valid
+ # anti-meridian-crossing version, we can apply
+ # split_across_anti_meridian to get valid non-anti-meridian-crossing
+ # shapes, our original goal.
+ context 'make invalid anti-meridian-crossing shapes valid and non-anti-meridian-crossing' do
+ specify 'splits into 3 pieces' do
+ mp = GeographicItem
+ .make_valid_non_anti_meridian_crossing_shape(devils_bowtie)
+
+ expect(mp.geometry_type.to_s).to eq('MultiPolygon')
+ expect(mp.num_geometries).to eq(3)
+ end
+
+ specify 'split pieces are valid' do
+ mp = GeographicItem
+ .make_valid_non_anti_meridian_crossing_shape(devils_bowtie)
+
+ mp.each do |p|
+ expect(p.valid?).to be true
+ end
+ end
+
+ specify "results don't cross the anti-meridian" do
+ mp = GeographicItem
+ .make_valid_non_anti_meridian_crossing_shape(devils_bowtie)
+
+ mp.each do |p|
+ expect(GeographicItem.crosses_anti_meridian?(p.as_text)).to be false
+ end
+ expect(GeographicItem.crosses_anti_meridian?(mp.as_text)).to be false
+ end
+
+ specify 'split pieces include expected points' do
+ mp = GeographicItem
+ .make_valid_non_anti_meridian_crossing_shape(devils_bowtie)
+
+ # the right piece
+ expect(mp[0].contains?(Gis::FACTORY.point(179, 5))).to be true
+ # the middle
+ expect(mp[1].contains?(Gis::FACTORY.point(-179, 5))).to be true
+ # the left piece
+ expect(mp[2].contains?(Gis::FACTORY.point(-161, 5))).to be true
+ end
+ end
+ end
end
end
end
diff --git a/spec/models/geographic_item/geometry_collection_spec.rb b/spec/models/geographic_item/geometry_collection_spec.rb
deleted file mode 100644
index 4ce99757c9..0000000000
--- a/spec/models/geographic_item/geometry_collection_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-RSpec.describe GeographicItem::GeometryCollection, type: :model, group: [:geo, :shared_geo] do
-
- include_context 'stuff for complex geo tests'
-
- context 'that this item' do
- let(:all_items) {
- FactoryBot.build(:geographic_item_geometry_collection,
- geometry_collection: all_shapes.as_binary)
- }
- context 'represents a geometry_collection' do
- specify 'type' do
- expect(all_items.type).to eq('GeographicItem::GeometryCollection')
- end
-
- specify 'valid?' do
- expect(all_items.valid?).to be_truthy
- end
-
- specify 'collection' do
- expect(all_items.geo_object.to_s).to eq('GEOMETRYCOLLECTION (MULTIPOINT ((-88.241421 40.091565 0.0), (-88.241417 40.09161 0.0), (-88.241413 40.091655 0.0)), POINT (0.0 0.0 0.0), POINT (-29.0 -16.0 0.0), POINT (-25.0 -18.0 0.0), POINT (-28.0 -21.0 0.0), POINT (-19.0 -18.0 0.0), POINT (3.0 -14.0 0.0), POINT (6.0 -12.9 0.0), POINT (5.0 -16.0 0.0), POINT (4.0 -17.9 0.0), POINT (7.0 -17.9 0.0), POINT (32.2 22.0 0.0), POINT (-17.0 7.0 0.0), POINT (-9.8 5.0 0.0), POINT (-10.7 0.0 0.0), POINT (-30.0 21.0 0.0), POINT (-25.0 18.3 0.0), POINT (-23.0 18.0 0.0), POINT (-19.6 -13.0 0.0), POINT (-7.6 14.2 0.0), POINT (-4.6 11.9 0.0), POINT (-8.0 -4.0 0.0), POINT (-4.0 -8.0 0.0), POINT (-10.0 -6.0 0.0), LINESTRING (-32.0 21.0 0.0, -25.0 21.0 0.0, -25.0 16.0 0.0, -21.0 20.0 0.0), POLYGON ((-14.0 23.0 0.0, -14.0 11.0 0.0, -2.0 11.0 0.0, -2.0 23.0 0.0, -8.0 21.0 0.0, -14.0 23.0 0.0), (-11.0 18.0 0.0, -8.0 17.0 0.0, -6.0 20.0 0.0, -4.0 16.0 0.0, -7.0 13.0 0.0, -11.0 14.0 0.0, -11.0 18.0 0.0)), MULTILINESTRING ((23.0 21.0 0.0, 16.0 21.0 0.0, 16.0 16.0 0.0, 11.0 20.0 0.0), (4.0 12.6 0.0, 16.0 12.6 0.0, 16.0 7.6 0.0), (21.0 12.6 0.0, 26.0 12.6 0.0, 22.0 17.6 0.0)), LINESTRING (-33.0 11.0 0.0, -24.0 4.0 0.0, -26.0 13.0 0.0, -38.0 14.0 0.0, -33.0 11.0 0.0), GEOMETRYCOLLECTION (POLYGON ((-19.0 9.0 0.0, -9.0 9.0 0.0, -9.0 2.0 0.0, -19.0 2.0 0.0, -19.0 9.0 0.0)), POLYGON ((5.0 -1.0 0.0, -14.0 -1.0 0.0, -14.0 6.0 0.0, 5.0 6.0 0.0, 5.0 -1.0 0.0)), POLYGON ((-11.0 -1.0 0.0, -11.0 -5.0 0.0, -7.0 -5.0 0.0, -7.0 -1.0 0.0, -11.0 -1.0 0.0)), POLYGON ((-3.0 -9.0 0.0, -3.0 -1.0 0.0, -7.0 -1.0 0.0, -7.0 -9.0 0.0, -3.0 -9.0 0.0)), POLYGON ((-7.0 -9.0 0.0, -7.0 -5.0 0.0, -11.0 -5.0 0.0, -11.0 -9.0 0.0, -7.0 -9.0 0.0))), MULTILINESTRING ((-20.0 -1.0 0.0, -26.0 -6.0 0.0), (-21.0 -4.0 0.0, -31.0 -4.0 0.0)), MULTIPOLYGON (((28.0 2.3 0.0, 23.0 -1.7 0.0, 26.0 -4.8 0.0, 28.0 2.3 0.0)), ((22.0 -6.8 0.0, 22.0 -9.8 0.0, 16.0 -6.8 0.0, 22.0 -6.8 0.0)), ((16.0 2.3 0.0, 14.0 -2.8 0.0, 18.0 -2.8 0.0, 16.0 2.3 0.0))), MULTIPOINT ((3.0 -14.0 0.0), (6.0 -12.9 0.0), (5.0 -16.0 0.0), (4.0 -17.9 0.0), (7.0 -17.9 0.0)), LINESTRING (27.0 -14.0 0.0, 18.0 -21.0 0.0, 20.0 -12.0 0.0, 25.0 -23.0 0.0), GEOMETRYCOLLECTION (MULTIPOLYGON (((28.0 2.3 0.0, 23.0 -1.7 0.0, 26.0 -4.8 0.0, 28.0 2.3 0.0)), ((22.0 -6.8 0.0, 22.0 -9.8 0.0, 16.0 -6.8 0.0, 22.0 -6.8 0.0)), ((16.0 2.3 0.0, 14.0 -2.8 0.0, 18.0 -2.8 0.0, 16.0 2.3 0.0))), MULTIPOINT ((3.0 -14.0 0.0), (6.0 -12.9 0.0), (5.0 -16.0 0.0), (4.0 -17.9 0.0), (7.0 -17.9 0.0)), LINESTRING (27.0 -14.0 0.0, 18.0 -21.0 0.0, 20.0 -12.0 0.0, 25.0 -23.0 0.0)), POLYGON ((-33.0 -11.0 0.0, -33.0 -23.0 0.0, -21.0 -23.0 0.0, -21.0 -11.0 0.0, -27.0 -13.0 0.0, -33.0 -11.0 0.0)), LINESTRING (-16.0 -15.5 0.0, -22.0 -20.5 0.0), POLYGON ((-1.0 1.0 0.0, 1.0 1.0 0.0, 1.0 -1.0 0.0, -1.0 -1.0 0.0, -1.0 1.0 0.0)), POLYGON ((-2.0 2.0 0.0, 2.0 2.0 0.0, 2.0 -2.0 0.0, -2.0 -2.0 0.0, -2.0 2.0 0.0)), POLYGON ((-3.0 3.0 0.0, 3.0 3.0 0.0, 3.0 -3.0 0.0, -3.0 -3.0 0.0, -3.0 3.0 0.0)), POLYGON ((-4.0 4.0 0.0, 4.0 4.0 0.0, 4.0 -4.0 0.0, -4.0 -4.0 0.0, -4.0 4.0 0.0)))')
- end
- end
-
- context 'that a geometry_collection knows how to emits its own hash' do
- specify 'points' do
- expect(all_items.rendering_hash[:points]).to eq [[-88.241421, 40.091565],
- [-88.241417, 40.09161],
- [-88.241413, 40.091655],
- [0.0, 0.0], [-29.0, -16.0], [-25.0, -18.0],
- [-28.0, -21.0], [-19.0, -18.0], [3.0, -14.0],
- [6.0, -12.9], [5.0, -16.0], [4.0, -17.9],
- [7.0, -17.9], [32.2, 22.0], [-17.0, 7.0],
- [-9.8, 5.0], [-10.7, 0.0], [-30.0, 21.0],
- [-25.0, 18.3], [-23.0, 18.0], [-19.6, -13.0],
- [-7.6, 14.2], [-4.6, 11.9], [-8.0, -4.0],
- [-4.0, -8.0], [-10.0, -6.0], [3.0, -14.0],
- [6.0, -12.9], [5.0, -16.0], [4.0, -17.9],
- [7.0, -17.9], [3.0, -14.0], [6.0, -12.9],
- [5.0, -16.0], [4.0, -17.9], [7.0, -17.9]]
- end
-
- specify 'lines' do
- expect(all_items.rendering_hash[:lines].to_s).to eq('[[[-32.0, 21.0], [-25.0, 21.0], [-25.0, 16.0], [-21.0, 20.0]], [[23.0, 21.0], [16.0, 21.0], [16.0, 16.0], [11.0, 20.0]], [[4.0, 12.6], [16.0, 12.6], [16.0, 7.6]], [[21.0, 12.6], [26.0, 12.6], [22.0, 17.6]], [[-33.0, 11.0], [-24.0, 4.0], [-26.0, 13.0], [-38.0, 14.0], [-33.0, 11.0]], [[-20.0, -1.0], [-26.0, -6.0]], [[-21.0, -4.0], [-31.0, -4.0]], [[27.0, -14.0], [18.0, -21.0], [20.0, -12.0], [25.0, -23.0]], [[27.0, -14.0], [18.0, -21.0], [20.0, -12.0], [25.0, -23.0]], [[-16.0, -15.5], [-22.0, -20.5]]]')
- end
-
- specify 'polygons' do
- expect(all_items.rendering_hash[:polygons]).to eq([[[-14.0, 23.0], [-14.0, 11.0], [-2.0, 11.0],
- [-2.0, 23.0], [-8.0, 21.0], [-14.0, 23.0]],
- [[-19.0, 9.0], [-9.0, 9.0], [-9.0, 2.0],
- [-19.0, 2.0], [-19.0, 9.0]],
- [[5.0, -1.0], [-14.0, -1.0], [-14.0, 6.0],
- [5.0, 6.0], [5.0, -1.0]],
- [[-11.0, -1.0], [-11.0, -5.0], [-7.0, -5.0],
- [-7.0, -1.0], [-11.0, -1.0]],
- [[-3.0, -9.0], [-3.0, -1.0], [-7.0, -1.0],
- [-7.0, -9.0], [-3.0, -9.0]],
- [[-7.0, -9.0], [-7.0, -5.0], [-11.0, -5.0],
- [-11.0, -9.0], [-7.0, -9.0]],
- [[28.0, 2.3], [23.0, -1.7], [26.0, -4.8], [28.0, 2.3]],
- [[22.0, -6.8], [22.0, -9.8], [16.0, -6.8], [22.0, -6.8]],
- [[16.0, 2.3], [14.0, -2.8], [18.0, -2.8], [16.0, 2.3]],
- [[28.0, 2.3], [23.0, -1.7], [26.0, -4.8], [28.0, 2.3]],
- [[22.0, -6.8], [22.0, -9.8], [16.0, -6.8], [22.0, -6.8]],
- [[16.0, 2.3], [14.0, -2.8], [18.0, -2.8], [16.0, 2.3]],
- [[-33.0, -11.0], [-33.0, -23.0], [-21.0, -23.0],
- [-21.0, -11.0], [-27.0, -13.0], [-33.0, -11.0]],
- [[-1.0, 1.0], [1.0, 1.0], [1.0, -1.0], [-1.0, -1.0],
- [-1.0, 1.0]],
- [[-2.0, 2.0], [2.0, 2.0], [2.0, -2.0], [-2.0, -2.0],
- [-2.0, 2.0]],
- [[-3.0, 3.0], [3.0, 3.0], [3.0, -3.0], [-3.0, -3.0],
- [-3.0, 3.0]],
- [[-4.0, 4.0], [4.0, 4.0], [4.0, -4.0], [-4.0, -4.0],
- [-4.0, 4.0]]])
- end
-
- specify 'all objects' do
- expect(all_items.rendering_hash.to_s).to eq('{:points=>[[-88.241421, 40.091565], [-88.241417, 40.09161], [-88.241413, 40.091655], [0.0, 0.0], [-29.0, -16.0], [-25.0, -18.0], [-28.0, -21.0], [-19.0, -18.0], [3.0, -14.0], [6.0, -12.9], [5.0, -16.0], [4.0, -17.9], [7.0, -17.9], [32.2, 22.0], [-17.0, 7.0], [-9.8, 5.0], [-10.7, 0.0], [-30.0, 21.0], [-25.0, 18.3], [-23.0, 18.0], [-19.6, -13.0], [-7.6, 14.2], [-4.6, 11.9], [-8.0, -4.0], [-4.0, -8.0], [-10.0, -6.0], [3.0, -14.0], [6.0, -12.9], [5.0, -16.0], [4.0, -17.9], [7.0, -17.9], [3.0, -14.0], [6.0, -12.9], [5.0, -16.0], [4.0, -17.9], [7.0, -17.9]], :lines=>[[[-32.0, 21.0], [-25.0, 21.0], [-25.0, 16.0], [-21.0, 20.0]], [[23.0, 21.0], [16.0, 21.0], [16.0, 16.0], [11.0, 20.0]], [[4.0, 12.6], [16.0, 12.6], [16.0, 7.6]], [[21.0, 12.6], [26.0, 12.6], [22.0, 17.6]], [[-33.0, 11.0], [-24.0, 4.0], [-26.0, 13.0], [-38.0, 14.0], [-33.0, 11.0]], [[-20.0, -1.0], [-26.0, -6.0]], [[-21.0, -4.0], [-31.0, -4.0]], [[27.0, -14.0], [18.0, -21.0], [20.0, -12.0], [25.0, -23.0]], [[27.0, -14.0], [18.0, -21.0], [20.0, -12.0], [25.0, -23.0]], [[-16.0, -15.5], [-22.0, -20.5]]], :polygons=>[[[-14.0, 23.0], [-14.0, 11.0], [-2.0, 11.0], [-2.0, 23.0], [-8.0, 21.0], [-14.0, 23.0]], [[-19.0, 9.0], [-9.0, 9.0], [-9.0, 2.0], [-19.0, 2.0], [-19.0, 9.0]], [[5.0, -1.0], [-14.0, -1.0], [-14.0, 6.0], [5.0, 6.0], [5.0, -1.0]], [[-11.0, -1.0], [-11.0, -5.0], [-7.0, -5.0], [-7.0, -1.0], [-11.0, -1.0]], [[-3.0, -9.0], [-3.0, -1.0], [-7.0, -1.0], [-7.0, -9.0], [-3.0, -9.0]], [[-7.0, -9.0], [-7.0, -5.0], [-11.0, -5.0], [-11.0, -9.0], [-7.0, -9.0]], [[28.0, 2.3], [23.0, -1.7], [26.0, -4.8], [28.0, 2.3]], [[22.0, -6.8], [22.0, -9.8], [16.0, -6.8], [22.0, -6.8]], [[16.0, 2.3], [14.0, -2.8], [18.0, -2.8], [16.0, 2.3]], [[28.0, 2.3], [23.0, -1.7], [26.0, -4.8], [28.0, 2.3]], [[22.0, -6.8], [22.0, -9.8], [16.0, -6.8], [22.0, -6.8]], [[16.0, 2.3], [14.0, -2.8], [18.0, -2.8], [16.0, 2.3]], [[-33.0, -11.0], [-33.0, -23.0], [-21.0, -23.0], [-21.0, -11.0], [-27.0, -13.0], [-33.0, -11.0]], [[-1.0, 1.0], [1.0, 1.0], [1.0, -1.0], [-1.0, -1.0], [-1.0, 1.0]], [[-2.0, 2.0], [2.0, 2.0], [2.0, -2.0], [-2.0, -2.0], [-2.0, 2.0]], [[-3.0, 3.0], [3.0, 3.0], [3.0, -3.0], [-3.0, -3.0], [-3.0, 3.0]], [[-4.0, 4.0], [4.0, 4.0], [4.0, -4.0], [-4.0, -4.0], [-4.0, 4.0]]]}')
- end
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(all_items.start_point).to eq([40.091565, -88.241421])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(all_items.st_start_point.to_s).to eq('POINT (-88.241421 40.091565 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item/line_string_spec.rb b/spec/models/geographic_item/line_string_spec.rb
deleted file mode 100644
index e99f7a781a..0000000000
--- a/spec/models/geographic_item/line_string_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::LineString, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:a) { FactoryBot.create(:geographic_item_line_string, line_string: shape_a1.as_binary) }
- specify 'represents a line_string' do
- expect(a.type).to eq('GeographicItem::LineString')
- expect(a.valid?).to be_truthy
- expect(a.geo_object.to_s).to eq('LINESTRING (-32.0 21.0 0.0, -25.0 21.0 0.0, -25.0 16.0 0.0, -21.0 20.0 0.0)')
- end
-
- specify 'that a line_string knows how to emits its own hash' do
- expect(a.rendering_hash).to eq(lines: [[[-32.0, 21.0], [-25.0, 21.0], [-25.0, 16.0], [-21.0, 20.0]]])
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(a.start_point).to eq([21.0, -32.0])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(a.st_start_point.to_s).to eq('POINT (-32.0 21.0 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item/multi_line_string_spec.rb b/spec/models/geographic_item/multi_line_string_spec.rb
deleted file mode 100644
index e23d0d2ec0..0000000000
--- a/spec/models/geographic_item/multi_line_string_spec.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::MultiLineString, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:c) { FactoryBot.build(:geographic_item_multi_line_string, multi_line_string: shape_c.as_binary) }
- specify 'represents a multi_line_string' do
- expect(c.type).to eq('GeographicItem::MultiLineString')
- expect(c.valid?).to be_truthy
- expect(c.geo_object.to_s).to eq('MULTILINESTRING ((23.0 21.0 0.0, 16.0 21.0 0.0, 16.0 16.0 0.0, ' \
- '11.0 20.0 0.0), (4.0 12.6 0.0, 16.0 12.6 0.0, 16.0 7.6 0.0), ' \
- '(21.0 12.6 0.0, 26.0 12.6 0.0, 22.0 17.6 0.0))')
- end
-
- specify 'for a multi_line_string' do
- expect(c.rendering_hash).to eq(lines: [[[23.0, 21.0], [16.0, 21.0], [16.0, 16.0], [11.0, 20.0]],
- [[4.0, 12.6], [16.0, 12.6], [16.0, 7.6]],
- [[21.0, 12.6], [26.0, 12.6], [22.0, 17.6]]])
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(c.start_point).to eq([21.0, 23.0])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(c.st_start_point.to_s).to eq('POINT (23.0 21.0 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item/multi_point_spec.rb b/spec/models/geographic_item/multi_point_spec.rb
deleted file mode 100644
index ab7ce3b319..0000000000
--- a/spec/models/geographic_item/multi_point_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::MultiPoint, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:rooms) { FactoryBot.build(:geographic_item_multi_point, multi_point: rooms20nn.as_binary) }
- specify 'represents a multi_point' do
- expect(rooms.type).to eq('GeographicItem::MultiPoint')
- expect(rooms.valid?).to be_truthy
- expect(rooms.geo_object.to_s).to eq('MULTIPOINT ((-88.241421 40.091565 0.0), ' \
- '(-88.241417 40.09161 0.0), (-88.241413 40.091655 0.0))')
- end
-
- specify 'for a multi_point' do
- expect(rooms.rendering_hash).to eq(points: [[-88.241421, 40.091565],
- [-88.241417, 40.09161],
- [-88.241413, 40.091655]])
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(rooms.start_point).to eq([40.091565, -88.241421])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(rooms.st_start_point.to_s).to eq('POINT (-88.241421 40.091565 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item/multi_polygon_spec.rb b/spec/models/geographic_item/multi_polygon_spec.rb
deleted file mode 100644
index 133c3ce655..0000000000
--- a/spec/models/geographic_item/multi_polygon_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::MultiPolygon, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:g) { FactoryBot.build(:geographic_item_multi_polygon, multi_polygon: shape_g.as_binary) }
- specify 'represents a multi_polygon' do
- expect(g.type).to eq('GeographicItem::MultiPolygon')
- expect(g.valid?).to be_truthy
- expect(g.geo_object.to_s).to eq('MULTIPOLYGON (((28.0 2.3 0.0, 23.0 -1.7 0.0, ' \
- '26.0 -4.8 0.0, 28.0 2.3 0.0)), ((22.0 -6.8 0.0, ' \
- '22.0 -9.8 0.0, 16.0 -6.8 0.0, 22.0 -6.8 0.0)), ' \
- '((16.0 2.3 0.0, 14.0 -2.8 0.0, 18.0 -2.8 0.0, 16.0 2.3 0.0)))')
- end
-
- specify 'for a multi_polygon' do
- expect(g.rendering_hash).to eq(polygons: [[[28.0, 2.3], [23.0, -1.7], [26.0, -4.8], [28.0, 2.3]],
- [[22.0, -6.8], [22.0, -9.8], [16.0, -6.8], [22.0, -6.8]],
- [[16.0, 2.3], [14.0, -2.8], [18.0, -2.8], [16.0, 2.3]]])
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(g.start_point).to eq([2.3, 28.0])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(g.st_start_point.to_s).to eq('POINT (28.0 2.3 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item/point_spec.rb b/spec/models/geographic_item/point_spec.rb
deleted file mode 100644
index 843d241170..0000000000
--- a/spec/models/geographic_item/point_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::Point, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:r2024) { FactoryBot.build(:geographic_item_point, point: room2024.as_binary) }
-
- specify 'represents a point' do
- expect(r2024.type).to eq('GeographicItem::Point')
- expect(r2024.valid?).to be_truthy
- expect(r2024.geo_object.to_s).to eq('POINT (-88.241413 40.091655 0.0)')
- end
-
- specify 'knows how to emits its own hash' do
- expect(r2024.rendering_hash).to eq(points: [[-88.241413, 40.091655]])
- end
-
- specify 'start_point returns a lat/lng of the first point of the GeoObject' do
- expect(r2024.start_point).to eq([40.091655, -88.241413])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(r2024.st_start_point.to_s).to eq('POINT (-88.241413 40.091655 0.0)')
- end
- end
-end
diff --git a/spec/models/geographic_item/polygon_spec.rb b/spec/models/geographic_item/polygon_spec.rb
deleted file mode 100644
index 60cdaf1082..0000000000
--- a/spec/models/geographic_item/polygon_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'rails_helper'
-require 'support/shared_contexts/shared_geo'
-
-describe GeographicItem::Polygon, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
- context 'that this item' do
- let(:k) { FactoryBot.build(:geographic_item_polygon, polygon: shape_k.as_binary) }
- specify 'represents a polygon' do
- expect(k.type).to eq('GeographicItem::Polygon')
- expect(k.valid?).to be_truthy
- expect(k.geo_object.to_s).to eq('POLYGON ((-33.0 -11.0 0.0, -33.0 -23.0 0.0, -21.0 -23.0 0.0, ' \
- '-21.0 -11.0 0.0, -27.0 -13.0 0.0, -33.0 -11.0 0.0))')
- end
-
- specify 'that a polygon knows how to emits its own hash' do
- expect(k.rendering_hash).to eq(polygons: [[[-33.0, -11.0], [-33.0, -23.0], [-21.0, -23.0],
- [-21.0, -11.0], [-27.0, -13.0], [-33.0, -11.0]]])
- end
-
- specify 'returns a lat/lng of the first point of the GeoObject' do
- expect(k.start_point).to eq([-11.0, -33.0])
- end
-
- specify '#st_start_point returns the first POINT of the GeoObject' do
- expect(k.st_start_point.to_s).to eq('POINT (-33.0 -11.0 0.0)')
- end
-
- end
-end
diff --git a/spec/models/geographic_item_spec.rb b/spec/models/geographic_item_spec.rb
index d6710fbf74..405d2744e1 100644
--- a/spec/models/geographic_item_spec.rb
+++ b/spec/models/geographic_item_spec.rb
@@ -1,1118 +1,875 @@
require 'rails_helper'
+require 'support/shared_contexts/shared_geo'
describe GeographicItem, type: :model, group: [:geo, :shared_geo] do
- include_context 'stuff for complex geo tests'
+ include_context 'stuff for GeographicItem tests' # that's spec/support/shared_contexts/shared_basic_geo.rb
- let(:geographic_item) { GeographicItem.new }
-
- let(:geo_json) {
- '{
- "type": "Feature",
- "geometry": {
- "type": "Point",
- "coordinates": [10, 10]
- },
- "properties": {
- "name": "Sample Point",
- "description": "This is a sample point feature."
- }
- }'
- }
-
- let(:geo_json2) {
- '{
- "type": "Feature",
- "geometry": {
- "type": "Point",
- "coordinates": [20, 20]
- },
- "properties": {
- "name": "Sample Point",
- "description": "This is a sample point feature."
- }
- }'
- }
+ # the pattern `before { [s1, s2, ...].each }` is to instantiate variables
+ # that have been `let` (not `let!`) by referencing them using [...].each.
+ # Shapes that were FactoryBot.created in `let`s will be saved to the
+ # database at that time, so you can specify your shapes universe for a given
+ # context by listing the shapes you want to exist in that universe.
- specify '#shape=' do
- g = GeographicItem.new(shape: geo_json)
- expect(g.save).to be_truthy
- end
-
- specify '#shape= 2' do
- g = GeographicItem.create!(shape: geo_json)
- g.update(shape: geo_json2)
- expect(g.reload.geo_object.to_s).to match(/20/)
- end
+ # !! Note that multi-shapes instantiate their constituent shapes, so for
+ # example if you reference (and therefore instantiate)
+ # donut_rectangle_multi_polygon, you've also instantiated donut and rectangle.
- specify '#shape= bad linear ring' do
- bad = '{
- "type": "Feature",
- "geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [-80.498221, 25.761437],
- [-80.498221, 25.761959],
- [-80.498221, 25.761959],
- [-80.498221, 25.761437]
- ]
- ]
- },
- "properties": {}
- }'
-
- g = GeographicItem.new(shape: bad)
- g.valid?
- expect(g.errors[:base]).to be_present
- end
-
- context 'using ce_test_objects' do
- let(:geographic_item) { FactoryBot.build(:geographic_item) }
- let(:geographic_item_with_point_a) { FactoryBot.build(:geographic_item_with_point_a) }
- let(:geographic_item_with_point_b) { FactoryBot.build(:geographic_item_with_point_b) }
- let(:geographic_item_with_point_c) { FactoryBot.build(:geographic_item_with_point_c) }
- let(:geographic_item_with_line_string) { FactoryBot.build(:geographic_item_with_line_string) }
- let(:geographic_item_with_polygon) { FactoryBot.build(:geographic_item_with_polygon) }
- let(:geographic_item_with_multi_polygon) { FactoryBot.build(:geographic_item_with_multi_polygon) }
-
-=begin
- context 'database functions' do
-
- specify 'ST_Geometry_Same' do
- skip
- #expect(GeographicItem.same(geographic_item_with_line_string.geo_object,
- # geographic_item_with_line_string.geo_object)).to be_truthy
- #expect(GeographicItem.same(geographic_item_with_line_string.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
- end
-
- specify 'ST_Area' do
- skip
- #expect(GeographicItem.area(geographic_item_with_polygon.geo_object)).to eq 0.123
- end
-
- specify 'ST_Azimuth' do
- skip
- #expect(GeographicItem.azimuth(geographic_item_with_point_a.geo_object,
- # geographic_item_with_point_b.geo_object)).to eq 44.5
- #expect(GeographicItem.azimuth(geographic_item_with_point_b.geo_object,
- # geographic_item_with_point_a.geo_object)).to eq 44.5
- #expect(GeographicItem.azimuth(geographic_item_with_point_a.geo_object,
- # geographic_item_with_point_a.geo_object)).to eq 44.5
- end
-
- specify 'ST_Centroid' do
- skip
- #expect(GeographicItem.centroid(geographic_item_with_polygon.polygon)).to eq geographic_item_with_point_c
- end
-
- specify 'ST_Contains' do
- skip
- #expect(GeographicItem.contains(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.contains(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
- #expect(GeographicItem.contains(geographic_item_with_polygon.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_truthy
- end
-
- specify 'self.find_contains ' do
- skip 'building a City of Champaign shape, and a point inside it'
- end
+ let(:geographic_item) { GeographicItem.new }
- specify 'ST_ContainsProperly ' do
- skip
- #expect(GeographicItem.contains_properly(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.contains_properly(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ context 'can hold any' do
+ specify 'point' do
+ expect(simple_point.geo_object_type).to eq(:point)
end
- specify 'ST_Covers' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'line_string' do
+ expect(simple_line_string.geo_object_type).to eq(:line_string)
end
- specify 'ST_CoveredBy' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'polygon' do
+ expect(simple_polygon.geo_object_type).to eq(:polygon)
end
- specify 'ST_Crosses' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'multi_point' do
+ expect(simple_multi_point.geo_object_type).to eq(:multi_point)
end
- specify 'ST_LineCrossingDirection' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'multi_line_string' do
+ expect(simple_multi_line_string.geo_object_type).to eq(:multi_line_string)
end
- specify 'ST_Disjoint' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'multi_polygon' do
+ expect(simple_multi_polygon.geo_object_type).to eq(:multi_polygon)
end
- specify 'ST_Distance' do
- skip
- #expect(GeographicItem.covers(geographic_item_with_polygon.geo_object,
- # geographic_item_with_point_c.geo_object)).to be_truthy
- #expect(GeographicItem.covers(geographic_item_with_point_c.geo_object,
- # geographic_item_with_polygon.geo_object)).to be_falsey
+ specify 'geometry_collection' do
+ expect(simple_geometry_collection.geo_object_type)
+ .to eq(:geometry_collection)
end
-
end
-=end
-
- # TODO: remove, redundant with single Factory use
- specify 'Two different object types share the same factory.' do
- # r is the result of an intersection
- # p16 uses the config.default factory
- expect(r.factory.projection_factory).to eq(p16_on_a.factory.projection_factory)
- end
+ context 'initialization hooks' do
+ context 'winding' do
+ let(:cw_polygon) do
+ # exterior cw, interior ccw (both backwards)
+ p = 'POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), ' \
+ '(3 3, 6 3, 6 6, 3 6, 3 3))'
- context 'STI' do
- context 'type is set before validation when column is provided (assumes type is null)' do
- GeographicItem::DATA_TYPES.each do |t|
- specify "for #{t}" do
- geographic_item.send("#{t}=", simple_shapes[t])
- expect(geographic_item.valid?).to be_truthy
- expect(geographic_item.type).to eq("GeographicItem::#{t.to_s.camelize}")
- end
- end
- end
-
- context 'subclasses have a SHAPE_COLUMN set' do
- GeographicItem.descendants.each do |d|
- specify "for #{d}" do
- expect(d::SHAPE_COLUMN).to be_truthy
- end
- end
+ FactoryBot.create(:geographic_item, geography: p)
end
- specify '#geo_object_type' do
- expect(geographic_item).to respond_to(:geo_object_type)
- end
+ let(:ccw_cw_m_p) do
+ # First ccw, second cw
+ m_p = 'MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)),' \
+ '((20 0, 20 10, 30 10, 30 0, 20 0)))'
- specify '#geo_object_type when item not saved' do
- geographic_item.point = simple_shapes[:point]
- expect(geographic_item.geo_object_type).to eq(:point)
+ FactoryBot.create(:geographic_item, geography: m_p)
end
- end
-
- context 'validation' do
- before(:each) {
- geographic_item.valid?
- }
- specify 'some data must be provided' do
- expect(geographic_item.errors[:base]).to be_present
- end
+ specify 'polygon winding is ccw after save' do
+ expect(cw_polygon.geo_object.exterior_ring.ccw?).to eq(false)
+ expect(
+ cw_polygon.geo_object.interior_rings.map(&:ccw?).uniq
+ ).to eq([true])
- specify 'invalid data for point is invalid' do
- geographic_item.point = 'Some string'
- expect(geographic_item.valid?).to be_falsey
+ cw_polygon.save!
+ cw_polygon.reload
+ expect(cw_polygon.geo_object.exterior_ring.ccw?).to eq(true)
+ expect(
+ cw_polygon.geo_object.interior_rings.map(&:ccw?).uniq
+ ).to eq([false])
end
- specify 'a valid point is valid' do
- expect(geographic_item_with_point_a.valid?).to be_truthy
- end
+ specify 'multi_polygon winding is ccw after save' do
+ expect(ccw_cw_m_p.geo_object[0].exterior_ring.ccw?).to eq(true)
+ expect(ccw_cw_m_p.geo_object[1].exterior_ring.ccw?). to eq(false)
- specify 'A good point that didn\'t change.' do
- expect(geographic_item_with_point_a.point.x).to eq -88.241413
+ ccw_cw_m_p.save!
+ ccw_cw_m_p.reload
+ expect(ccw_cw_m_p.geo_object[0].exterior_ring.ccw?).to eq(true)
+ expect(ccw_cw_m_p.geo_object[1].exterior_ring.ccw?). to eq(true)
end
+ end
- specify 'a point, when provided, has a legal geography' do
- geographic_item.point = RSPEC_GEO_FACTORY.point(180.0, 85.0)
- expect(geographic_item.valid?).to be_truthy
+ context 'area' do
+ specify 'area of a saved polygon is > 0' do
+ expect(box.area).to be > 0
end
+ end
- specify 'One and only one of point, line_string, etc. is set.' do
- geographic_item_with_point_a.polygon = geographic_item_with_polygon.polygon
- expect(geographic_item_with_point_a.valid?).to be_falsey
+ context 'longitudes' do
+ specify 'longitude of a point should be stored between -180 and 180: case > 180' do
+ # GeoJSON.decode uses the default spherical factory and doesn't
+ # normalize longitudes (but is still srid 4326, which is what matters
+ # for saving to gi):
+ p = RGeo::GeoJSON.decode('{"type":"Point","coordinates":[200,10]}')
+ expect(p.x).to eq(200)
+
+ gi = GeographicItem.create!(geography: p)
+ # There's no way to get the geography point out of gi without creating a
+ # point using our factory, which itself normalizes longitudes, so
+ # retrieve straight from the database instead (which is really the point
+ # of the longitude normalization hook, and what we want to test here).
+ expect(ActiveRecord::Base.connection.select_value(
+ "SELECT ST_X(
+ (SELECT geography FROM geographic_items WHERE id = #{gi.id})::geometry
+ )"
+ )
+ ).to eq(-160)
+ end
+
+ specify 'longitude of a point should be stored between -180 and 180: case < -180' do
+ p = RGeo::GeoJSON.decode('{"type":"Point","coordinates":[-200,10]}')
+ expect(p.x).to eq(-200)
+
+ gi = GeographicItem.create!(geography: p)
+ expect(ActiveRecord::Base.connection.select_value(
+ "SELECT ST_X(
+ (SELECT geography FROM geographic_items WHERE id = #{gi.id})::geometry
+ )"
+ )
+ ).to eq(160)
end
end
+ end
- context 'geo_object interactions (Geographical attribute of GeographicItem)' do
-
- context 'Any line_string can be made into polygons.' do
- specify 'non-closed line string' do
- expect(RSPEC_GEO_FACTORY.polygon(list_k).to_s).to eq('POLYGON ((-33.0 -11.0 0.0, -33.0 -23.0 0.0, -21.0 -23.0 0.0, -21.0 -11.0 0.0, -27.0 -13.0 0.0, -33.0 -11.0 0.0))')
- end
+ context 'scopes (GeographicItems can be found by searching with)' do
+ before {
+ [ce_box_centroid, ce_rectangle_point,
+ gr_box_centroid, gr_rectangle_point, distant_point].each
+ }
- specify 'closed line string' do
- expect(RSPEC_GEO_FACTORY.polygon(d.geo_object).to_s).to eq('POLYGON ((-33.0 11.0 0.0, -24.0 4.0 0.0, -26.0 13.0 0.0, -38.0 14.0 0.0, -33.0 11.0 0.0))')
- end
- end
+ specify '::geo_with_collecting_event includes' do
+ expect(GeographicItem.geo_with_collecting_event.to_a)
+ .to contain_exactly(box_centroid, box_rectangle_intersection_point)
+ end
- specify 'That one object contains another, or not.' do
- expect(k.contains?(p1.geo_object)).to be_truthy
- end
+ specify '::geo_with_collecting_event does not include' do
+ expect(GeographicItem.geo_with_collecting_event.to_a)
+ .not_to include(distant_point)
+ end
- specify 'That one object contains another, or not.' do
- expect(k.contains?(p17.geo_object)).to be_falsey
- end
+ specify '::err_with_collecting_event includes' do
+ expect(GeographicItem.err_with_collecting_event.to_a)
+ .to contain_exactly(box, rectangle_intersecting_box)
+ end
- specify 'That one object contains another, or not.' do
- expect(p17.within?(k.geo_object)).to be_falsey
- end
+ specify '::err_with_collecting_event does not include' do
+ expect(GeographicItem.err_with_collecting_event.to_a)
+ .not_to include(distant_point)
+ end
- specify 'That one object contains another, or not.' do
- expect(p17.within?(k.geo_object)).to be_falsey
- end
+ specify '::include_collecting_event' do
+ # This is just all created GeographicItems (doesn't test preloading)
+ expect(GeographicItem.include_collecting_event.to_a)
+ .to contain_exactly(box_centroid, box, distant_point,
+ rectangle_intersecting_box, box_rectangle_intersection_point)
+ end
- specify 'That one object intersects another, or not.' do # using geographic_item.intersects?
- expect(e1.intersects?(e2.geo_object)).to be_truthy
- end
+ specify '::with_collecting_event_through_georeferences includes' do
+ expect(GeographicItem.with_collecting_event_through_georeferences.to_a)
+ .to contain_exactly(box_centroid, box_rectangle_intersection_point,
+ box, rectangle_intersecting_box)
+ end
- specify 'That one object intersects another, or not.' do # using geographic_item.intersects?
- expect(e1.intersects?(e3.geo_object)).to be_falsey
- end
+ specify '::with_collecting_event_through_georeferences does not contain' do
+ expect(GeographicItem.with_collecting_event_through_georeferences.to_a)
+ .not_to include(distant_point)
+ end
+ end
- specify 'That one object intersects another, or not.' do # using geographic_item.intersects?
- expect(p1.intersects?(k.geo_object)).to be_truthy
- end
+ context 'construction via #shape=' do
+ let(:geo_json) {
+ '{
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [10, 10]
+ },
+ "properties": {
+ "name": "Sample Point",
+ "description": "This is a sample point feature."
+ }
+ }'
+ }
+
+ let(:geo_json2) {
+ '{
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [20, 20]
+ },
+ "properties": {
+ "name": "Sample Point",
+ "description": "This is a sample point feature."
+ }
+ }'
+ }
- specify 'That one object intersects another, or not.' do # using geographic_item.intersects?
- expect(p17.intersects?(k.geo_object)).to be_falsey
- end
+ specify '#shape=' do
+ g = GeographicItem.new(shape: geo_json)
+ expect(g.save).to be_truthy
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.intersects?(shapeE2)).to be_truthy
- end
+ specify '#shape= 2' do
+ g = GeographicItem.create!(shape: geo_json)
+ g.update!(shape: geo_json2)
+ expect(g.reload.geo_object.to_s).to match(/20/)
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.intersects?(shapeE3)).to be_falsey
- end
+ specify '#shape= bad linear ring' do
+ bad = '{
+ "type": "Feature",
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [-80.498221, 25.761437],
+ [-80.498221, 25.761959],
+ [-80.498221, 25.761959],
+ [-80.498221, 25.761437]
+ ]
+ ]
+ }
+ }'
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.overlaps?(shapeE2)).to be_truthy
- end
+ g = GeographicItem.new(shape: bad)
+ g.valid?
+ expect(g.errors[:base]).to be_present
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.overlaps?(shapeE3)).to be_falsey
- end
+ specify 'for polygon' do
+ geographic_item.shape = '{"type":"Feature","geometry":{"type":"Polygon",' \
+ '"coordinates":[[[-90.25122106075287,38.619731572825145],[-86.12036168575287,39.77758382625017],' \
+ '[-87.62384042143822,41.89478088863241],[-90.25122106075287,38.619731572825145]]]}}'
+ expect(geographic_item.valid?).to be_truthy
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.intersection(shapeE2)).to eq(e1_and_e2)
- end
+ specify 'for linestring' do
+ geographic_item.shape =
+ '{"type":"Feature","geometry":{"type":"LineString","coordinates":[' \
+ '[-90.25122106075287,38.619731572825145],' \
+ '[-86.12036168575287,39.77758382625017],' \
+ '[-87.62384042143822,41.89478088863241]]}}'
+ expect(geographic_item.valid?).to be_truthy
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.intersection(shapeE4)).to eq(e1_and_e4)
- end
+ specify 'for "circle"' do
+ geographic_item.shape = '{"type":"Feature","geometry":{"type":"Point",' \
+ '"coordinates":[-88.09681320155505,40.461195702960666]},' \
+ '"properties":{"radius":1468.749413840412,' \
+ '"name":"Paxton City Hall"}}'
+ expect(geographic_item.valid?).to be_truthy
+ end
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.union(shapeE2)).to eq(e1_or_e2)
- end
+ context '#geo_object_type gives underlying shape' do
+ specify '#geo_object_type' do
+ expect(geographic_item).to respond_to(:geo_object_type)
+ end
- specify 'Two polygons may have various intersections.' do
- expect(shapeE1.union(shapeE5)).to eq(e1_or_e5)
- end
+ specify '#geo_object_type when item not saved' do
+ geographic_item.geography = simple_shapes[:point]
+ expect(geographic_item.geo_object_type).to eq(:point)
+ end
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE1.touches?(shapeE5)).to be_falsey
- end
+ context 'validation' do
+ specify 'some data must be provided' do
+ geographic_item.valid?
+ expect(geographic_item.errors[:base]).to be_present
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE2.touches?(shapeE3)).to be_truthy
- end
+ specify 'invalid data for point is invalid' do
+ geographic_item.geography = 'Some string'
+ expect(geographic_item.valid?).to be_falsey
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE2.touches?(shapeE5)).to be_falsey
- end
+ specify 'a valid point is valid' do
+ expect(simple_point.valid?).to be_truthy
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE1.disjoint?(shapeE5)).to be_truthy
- end
+ specify "a good point didn't change on creation" do
+ expect(simple_point.geography.x).to eq 10
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE2.disjoint?(shapeE5)).to be_truthy
- end
+ specify 'a point, when provided, has a legal geography' do
+ geographic_item.geography = simple_point.geo_object
+ expect(geographic_item.valid?).to be_truthy
+ end
- specify 'Two polygons may have various adjacencies.' do
- expect(shapeE2.disjoint?(shapeE4)).to be_falsey
- end
+ specify 'geography can change shape' do
+ simple_point.geography = simple_polygon.geography
+ expect(simple_point.valid?).to be_truthy
+ expect(simple_point.geo_object_type).to eq(:polygon)
+ end
+ end
- specify 'Two different object types have various intersections.' do
- # Now that these are the same factory the equivalence is the "same"
- expect(r).to eq(p16_on_a)
- end
+ context 'instance methods' do
- specify 'Two different object types have various intersections.' do
- expect(l.geo_object.intersects?(k.geo_object)).to be_truthy
- end
+ context 'rgeo through geo_object' do
+ context '#geo_object' do
+ before {
+ geographic_item.geography = simple_point.geo_object
+ }
- specify 'Two different object types have various intersections.' do
- expect(l.geo_object.intersects?(e.geo_object)).to be_falsey
- end
+ specify '#geo_object' do
+ expect(geographic_item).to respond_to(:geo_object)
+ end
- specify 'Two different object types have various intersections.' do
- expect(f.geo_object.geometry_n(0).intersection(f.geo_object.geometry_n(1))).to be_truthy
- end
+ specify '#geo_object returns stored data' do
+ geographic_item.save!
+ expect(geographic_item.geo_object).to eq(simple_point.geo_object)
+ end
- specify 'Objects can be related by distance' do
- expect(p17.geo_object.distance(k.geo_object)).to be < p10.geo_object.distance(k.geo_object)
+ specify '#geo_object returns stored db data' do
+ geographic_item.save!
+ geo_id = geographic_item.id
+ expect(GeographicItem.find(geo_id).geo_object)
+ .to eq geographic_item.geo_object
+ end
end
- specify 'Objects can be related by distance' do
- expect(k.near(p1.geo_object, 0)).to be_truthy
+ specify '#contains? - to see if one object is contained by another.' do
+ expect(geographic_item).to respond_to(:contains?)
end
- specify 'Objects can be related by distance' do
- expect(k.near(p17.geo_object, 2)).to be_truthy
+ specify '#within? - to see if one object is within another.' do
+ expect(geographic_item).to respond_to(:within?)
end
- specify 'Objects can be related by distance' do
- expect(k.near(p10.geo_object, 5)).to be_falsey
+ specify '#contains? if one object is inside the area defined by the other' do
+ expect(donut.contains?(donut_interior_point.geo_object)).to be_truthy
end
- specify 'Objects can be related by distance' do
- expect(k.far(p1.geo_object, 0)).to be_falsey
+ specify '#contains? if one object is outside the area defined by the other' do
+ expect(donut.contains?(distant_point.geo_object)).to be_falsey
end
+ end
- specify 'Objects can be related by distance' do
- expect(k.far(p17.geo_object, 1)).to be_truthy
+ context 'centroids' do
+ context '#st_centroid' do
+ specify '#st_centroid returns wkt of the centroid' do
+ # geometric centroid
+ expect(simple_polygon.st_centroid).to eq('POINT(5 5)')
+ end
end
- specify 'Objects can be related by distance' do
- expect(k.far(p10.geo_object, 5)).to be_truthy
+ context '#centroid' do
+ # geometric centroid
+ specify '#centroid returns an rgeo centroid' do
+ expect(box.centroid).to eq(box_centroid.geo_object)
+ end
end
- specify 'Outer Limits' do
- expect(all_items.geo_object.convex_hull()).to eq(convex_hull)
+ context '#center_coords' do
+ specify 'works for a polygon' do
+ lat, long = box.center_coords
+ expect(lat.to_s).to eq(lat)
+ expect(long.to_s).to eq(long)
+ expect(lat.to_f).to be_within(0.01).of(box_centroid.geo_object.y)
+ expect(long.to_f).to be_within(0.01).of(box_centroid.geo_object.x)
+ end
end
end
- context 'That GeographicItems provide certain methods.' do
- before {
- geographic_item.point = room2024
- geographic_item.valid?
- }
- specify 'self.geo_object returns stored data' do
- expect(geographic_item.save!).to be_truthy
- end
-
- specify 'self.geo_object returns stored data' do
- geographic_item.save!
- _geo_id = geographic_item.id
- expect(geographic_item.geo_object).to eq(room2024)
- end
-
- specify 'self.geo_object returns stored data' do
- geographic_item.save!
- geo_id = geographic_item.id
- expect(GeographicItem.find(geo_id).geo_object).to eq geographic_item.geo_object
+ context '#st_distance_to_geographic_item' do
+ specify 'works for distance beetween points on equator' do
+ expect(
+ equator_point_long_0
+ .st_distance_to_geographic_item(equator_point_long_20)
+ ).to be_within(0.01).of(20 * Utilities::Geo::ONE_WEST)
end
end
- context 'instance methods' do
- specify '#geo_object' do
- expect(geographic_item).to respond_to(:geo_object)
+ context '#st_is_valid' do
+ specify 'valid polygon is valid' do
+ expect(simple_polygon.st_is_valid).to be true
end
- specify '#contains? - to see if one object is contained by another.' do
- expect(geographic_item).to respond_to(:contains?)
- end
+ specify 'bowtie polygon is invalid' do
+ bowtie = 'POLYGON((0 0, 10 10, 10 0, 0 10, 0 0))'
+ geographic_item.geography = bowtie
+ geographic_item.save!
- specify '#within? - to see if one object is within another.' do
- expect(geographic_item).to respond_to(:within?)
+ expect(geographic_item.st_is_valid).to be false
end
- specify '#near' do
- expect(geographic_item).to respond_to(:near)
- end
+ specify '#st_is_valid_reason returns an invalid reason' do
+ bowtie = 'POLYGON((0 0, 10 10, 10 0, 0 10, 0 0))'
+ geographic_item.geography = bowtie
+ geographic_item.save!
- specify '#far' do
- expect(geographic_item).to respond_to(:far)
+ expect(geographic_item.st_is_valid_reason)
+ .to include('Self-intersection')
end
+ end
+ end
- specify '#contains? if one object is inside the area defined by the other (watch out for holes)' do
- expect(k.contains?(p1.geo_object)).to be_truthy
- end
+ context 'class methods' do
- specify '#contains? if one object is inside the area defined by the other (watch out for holes)' do
- expect(e1.contains?(p10.geo_object)).to be_falsey
- end
+ context '::superset_of_union_of - return objects containing the union of the given objects' do
+ before {
+ [donut, donut_centroid, donut_interior_point,
+ donut_left_interior_edge_point, donut_left_interior_edge,
+ donut_bottom_interior_edge,
+ donut_interior_bottom_left_multi_line].each
+ }
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(p0.st_npoints).to eq(1)
+ specify "doesn't return self" do
+ expect(GeographicItem.superset_of_union_of(
+ donut.id
+ ).to_a).to eq([])
end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(a.st_npoints).to eq(4)
+ specify 'find the polygon containing the point' do
+ expect(GeographicItem.superset_of_union_of(
+ donut_interior_point.id
+ ).to_a).to contain_exactly(donut)
end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(b.st_npoints).to eq(13)
+ specify 'tests against the *union* of its inputs' do
+ expect(GeographicItem.superset_of_union_of(
+ donut_interior_point.id, distant_point.id
+ ).to_a).to eq([])
end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(h.st_npoints).to eq(5)
+ specify 'polygon containing two of its points is returned once' do
+ expect(GeographicItem.superset_of_union_of(
+ [donut_interior_point.id, donut_left_interior_edge_point.id]
+ ).to_a).to contain_exactly(donut)
end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(f.st_npoints).to eq(4)
+ specify 'a polygon covers its edge' do
+ expect(GeographicItem.superset_of_union_of(
+ donut_interior_bottom_left_multi_line.id
+ ).to_a).to contain_exactly(donut)
end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(g.st_npoints).to eq(12)
- end
+ specify 'find that shapes contain their vertices' do
+ vertex = FactoryBot.create(:geographic_item,
+ geography: donut_left_interior_edge.geo_object.start_point)
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(all_items.st_npoints).to eq(157)
+ expect(GeographicItem.superset_of_union_of(
+ vertex.id
+ ).to_a).to contain_exactly(donut,
+ donut_left_interior_edge, donut_bottom_interior_edge,
+ donut_interior_bottom_left_multi_line
+ )
end
+ end
- specify '#st_npoints returns the number of included points for a valid GeoItem' do
- expect(outer_limits.st_npoints).to eq(7)
- end
+ context '::subset_of_union_of' do
+ before { [donut_interior_bottom_left_multi_line,
+ donut_interior_point, donut_centroid,
+ donut_left_interior_edge, donut_bottom_interior_edge].each
+ }
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(p0.valid_geometry?).to be_truthy
+ specify 'a shape is subset_of_union_of itself' do
+ expect(
+ GeographicItem.where(
+ GeographicItem.subset_of_union_of_sql(donut.id)
+ ).to_a
+ ).to include(donut)
+ end
+
+ specify 'finds the shapes covered by a polygon' do
+ expect(
+ GeographicItem.where(
+ GeographicItem.subset_of_union_of_sql(donut.id)
+ ).to_a
+ ).to contain_exactly(donut, donut_interior_bottom_left_multi_line,
+ donut_left_interior_edge, donut_bottom_interior_edge,
+ donut_interior_point)
+ end
+
+ specify 'returns duplicates' do
+ duplicate_point = FactoryBot.create(:geographic_item,
+ geography: box_centroid.geo_object)
+
+ expect(
+ GeographicItem.where(
+ GeographicItem.subset_of_union_of_sql(box.id)
+ ).to_a
+ ).to contain_exactly(box_centroid, duplicate_point, box)
+ end
+
+ specify 'supports multiple input ids' do
+ expect(
+ GeographicItem.where(
+ GeographicItem.subset_of_union_of_sql(
+ donut_bottom_interior_edge.id, donut_left_interior_edge.id
+ )
+ ).to_a
+ ).to contain_exactly(donut_bottom_interior_edge,
+ donut_left_interior_edge, donut_interior_bottom_left_multi_line)
end
+ end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(a.valid_geometry?).to be_truthy
+ context '::st_covers - returns objects of a given shape which contain one
+ or more given objects' do
+ before { [donut, donut_left_interior_edge,
+ donut_interior_bottom_left_multi_line,
+ donut_rectangle_multi_polygon,
+ box, box_rectangle_union
+ ].each }
+
+ specify 'includes self when self is of the right shape' do
+ expect(GeographicItem.st_covers('multi_line_string',
+ [donut_interior_bottom_left_multi_line]).to_a)
+ .to include(donut_interior_bottom_left_multi_line)
+ end
+
+ specify 'a shape that covers two input shapes is only returned once' do
+ expect(GeographicItem.st_covers('polygon',
+ [box_centroid, box_horizontal_bisect_line]).to_a)
+ # box and box_rectangle_union contain both inputs
+ .to contain_exactly(
+ box, rectangle_intersecting_box, box_rectangle_union
+ )
+ end
+
+ specify 'includes shapes that cover part of their boundary' do
+ expect(GeographicItem.st_covers('any',
+ [donut_left_interior_edge]).to_a)
+ .to contain_exactly(donut_left_interior_edge,
+ donut_interior_bottom_left_multi_line, donut,
+ donut_rectangle_multi_polygon
+ )
+ end
+
+ specify 'point covered by nothing is only covered by itself' do
+ expect(GeographicItem.st_covers('any',
+ distant_point).to_a)
+ .to contain_exactly(distant_point)
+ end
+
+ # OR!
+ specify 'disjoint polygons each containing an input' do
+ expect(GeographicItem.st_covers('polygon',
+ [donut_left_interior_edge, box_centroid]).to_a)
+ .to contain_exactly(
+ donut, box, rectangle_intersecting_box, box_rectangle_union
+ )
+ end
+
+ specify 'works with any_line' do
+ expect(GeographicItem.st_covers('any_line',
+ donut_left_interior_edge_point, distant_point).to_a)
+ .to contain_exactly(
+ donut_left_interior_edge,
+ donut_interior_bottom_left_multi_line
+ )
+ end
+
+ specify 'works with any_poly' do
+ expect(GeographicItem.st_covers('any_poly',
+ box_centroid).to_a)
+ .to contain_exactly(
+ box, rectangle_intersecting_box, box_rectangle_union,
+ donut_rectangle_multi_polygon
+ )
+ end
+
+ specify 'works with any' do
+ expect(GeographicItem.st_covers('any',
+ donut_left_interior_edge_point).to_a)
+ .to contain_exactly(donut_left_interior_edge_point,
+ donut_left_interior_edge,
+ donut_interior_bottom_left_multi_line,
+ donut,
+ donut_rectangle_multi_polygon
+ )
end
+ end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(b.valid_geometry?).to be_truthy
+ context '::st_covered_by - returns objects which are contained by given
+ objects.' do
+ before { [donut, donut_interior_point, donut_left_interior_edge_point,
+ donut_left_interior_edge_point, donut_left_interior_edge,
+ donut_bottom_interior_edge,
+ donut_interior_bottom_left_multi_line,
+ box, box_centroid, box_horizontal_bisect_line,
+ rectangle_intersecting_box, box_rectangle_intersection_point,
+ donut_rectangle_multi_polygon].each }
+
+ specify 'object of the right shape is st_covered_by itself' do
+ expect(GeographicItem.st_covered_by('multi_line_string',
+ [donut_interior_bottom_left_multi_line]).to_a)
+ .to include(donut_interior_bottom_left_multi_line)
+ end
+
+ specify 'includes shapes which are a boundary component of an input' do
+ expect(GeographicItem.st_covered_by('line_string',
+ donut).to_a)
+ .to contain_exactly(donut_left_interior_edge,
+ donut_bottom_interior_edge)
+ end
+
+ specify 'a point only covers itself' do
+ expect(GeographicItem.st_covered_by('any',
+ donut_left_interior_edge_point).to_a)
+ .to eq([donut_left_interior_edge_point])
+ end
+
+ specify 'shapes contained by two shapes are only returned once' do
+ expect(GeographicItem.st_covered_by('point',
+ box, rectangle_intersecting_box).to_a)
+ .to eq([box_centroid, box_rectangle_intersection_point])
+ end
+
+ specify 'points in separate polygons' do
+ expect(GeographicItem.st_covered_by('point',
+ donut, box).to_a)
+ .to contain_exactly(donut_interior_point,
+ donut_left_interior_edge_point, box_centroid,
+ box_rectangle_intersection_point)
+ end
+
+ specify 'works with any_line' do
+ expect(GeographicItem.st_covered_by('any_line',
+ donut).to_a)
+ .to contain_exactly(
+ donut_left_interior_edge, donut_bottom_interior_edge,
+ donut_interior_bottom_left_multi_line
+ )
+ end
+
+ specify 'works with any_poly' do
+ expect(GeographicItem.st_covered_by('any_poly',
+ donut_rectangle_multi_polygon).to_a)
+ .to contain_exactly(
+ donut, rectangle_intersecting_box, donut_rectangle_multi_polygon
+ )
+ end
+
+ specify 'does not work with arbitrary geometry collection' do
+ pending 'ST_Covers fails when input GeometryCollection has a line intersecting a polygon\'s interior'
+ # The same test as the previous only the geometry collection in the
+ # first argument also contains a line intersecting the interior of
+ # rectangle - if you remove the line from the collection the test
+ # passes:
+ # pass_g_c = FactoryBot.create(:geographic_item_geography, geography:
+ # RSPEC_GEO_FACTORY.collection(
+ # [
+ # donut.geo_object,
+ # rectangle_intersecting_box.geo_object,
+ # ]
+ # )
+ # If the line only intersects the boundary of rectangle, or the line
+ # is interior to rectangle, or if it's instead an interior point of
+ # rectangle, the test also passes.
+ expect(GeographicItem.st_covered_by('any_poly',
+ donut_box_bisector_rectangle_geometry_collection).to_a)
+ .to contain_exactly(
+ donut, rectangle_intersecting_box, donut_rectangle_multi_polygon
+ )
+ end
+
+ specify 'works with any' do
+ expect(GeographicItem.st_covered_by('any',
+ box_rectangle_union).to_a)
+ .to contain_exactly(box_rectangle_union,
+ box, rectangle_intersecting_box, box_horizontal_bisect_line,
+ box_centroid, box_rectangle_intersection_point
+ )
end
+ end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(h.valid_geometry?).to be_truthy
+ context '::within_radius_of_item' do
+ before { [equator_point_long_0, equator_point_long_20,
+ equator_point_long_30].each }
+ specify 'returns objects within a specific distance of an object' do
+ r = 15 * Utilities::Geo::ONE_WEST
+ expect(
+ GeographicItem.within_radius_of_item(equator_point_long_20.id, r)
+ ).to contain_exactly(equator_point_long_20, equator_point_long_30)
end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(f.valid_geometry?).to be_truthy
+ specify "doesn't return objects outside a specific distance of an object" do
+ r = 5 * Utilities::Geo::ONE_WEST
+ expect(
+ GeographicItem.within_radius_of_item(equator_point_long_20.id, r)
+ ).to contain_exactly(equator_point_long_20)
end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(g.valid_geometry?).to be_truthy
+ # Intended?
+ specify 'shape is within_radius_of itself' do
+ expect(
+ GeographicItem.within_radius_of_item(distant_point.id, 100)
+ ).to include(distant_point)
end
+ end
- specify '#valid_geometry? returns \'true\' for a valid GeoObject' do
- expect(all_items.valid_geometry?).to be_truthy
- end
+ context '::intersecting' do
+ before { [
+ donut, donut_left_interior_edge,
+ box, box_centroid, box_horizontal_bisect_line,
+ rectangle_intersecting_box, box_rectangle_union
+ ].each }
- specify '#st_centroid returns a lat/lng of the centroid of the GeoObject' do
- expect(new_box_a.st_centroid).to eq('POINT(5 5)')
+ # Intended?
+ specify 'a geometry of the right shape intersects itself' do
+ expect(GeographicItem.intersecting('any', distant_point.id).to_a)
+ .to eq([distant_point])
end
- specify '#center_coords' do
- expect(new_box_a.center_coords).to eq(['5.000000', '5.000000'])
+ specify 'works with a specific shape' do
+ expect(GeographicItem.intersecting('polygon',
+ box_rectangle_intersection_point.id).to_a)
+ .to contain_exactly(
+ box, rectangle_intersecting_box, box_rectangle_union
+ )
end
- context '#shape on new' do
- let(:object) { GeographicItem.new }
- # '[40.190063612251016, -111.58300638198853]'
- specify 'for point' do
- object.shape = '{"type":"Feature","geometry":{"type":"Point",' \
- '"coordinates":[-88.0975631475394,40.45993808344767]},' \
- '"properties":{"name":"Paxton City Hall"}}'
- expect(object.valid?).to be_truthy
- end
-
- specify 'for polygon' do
- object.shape = '{"type":"Feature","geometry":{"type":"Polygon",' \
- '"coordinates":[[[-90.25122106075287,38.619731572825145],[-86.12036168575287,39.77758382625017],' \
- '[-87.62384042143822,41.89478088863241],[-90.25122106075287,38.619731572825145]]]},"properties":{}}'
- expect(object.valid?).to be_truthy
- end
-
- specify 'for linestring' do
- object.shape = '{"type":"Feature","geometry":{"type":"LineString","coordinates":[' \
- '[-90.25122106075287,38.619731572825145],' \
- '[-86.12036168575287,39.77758382625017],' \
- '[-87.62384042143822,41.89478088863241]]},"properties":{}}'
- expect(object.valid?).to be_truthy
- end
-
- specify 'for circle' do
- object.shape = '{"type":"Feature","geometry":{"type":"Point",' \
- '"coordinates":[-88.09681320155505,40.461195702960666]},' \
- '"properties":{"radius":1468.749413840412, "name":"Paxton City Hall"}}'
- expect(object.valid?).to be_truthy
- end
+ specify 'works with multiple input shapes' do
+ expect(GeographicItem.intersecting('line_string',
+ [donut_left_interior_edge_point.id, box.id]).to_a)
+ .to contain_exactly(
+ donut_left_interior_edge, box_horizontal_bisect_line
+ )
end
- context '#centroid' do
- specify 'for point' do
- expect(r2024.centroid.to_s).to eq('POINT (-88.241413 40.091655 0.0)')
- end
-
- specify 'for line_string' do
- expect(c1.centroid.to_s).to match(/POINT \(16\.461453\d* 19\.276957\d* 0\.0\)/)
- end
-
- specify 'for polygon' do
- expect(b.centroid.to_s).to match(/POINT \(-8\.091346\d* 16\.666666\d* 0\.0\)/)
- end
-
- specify 'for multi_point' do
- expect(h.centroid.to_s).to match(/POINT \(5\.0 -15\.7(4|399999\d*) 0\.0\)/) # TODO: Review the way this is being check (and the others too actually)
- end
-
- specify 'for multi_line_string' do
- expect(c.centroid.to_s).to match(/POINT \(16\.538756\d* 15\.300166\d* 0\.0\)/)
- end
-
- specify 'for multi_polygon' do
- expect(g.centroid.to_s).to match(/POINT \(21\.126454\d* -3.055235\d* 0\.0\)/)
- end
-
- specify 'for geometry_collection' do
- expect(j.centroid.to_s).to match(/POINT \(21\.126454\d* -3\.055235\d* 0\.0\)/)
- end
+ specify 'works with any' do
+ expect(GeographicItem.intersecting('any',
+ box_horizontal_bisect_line.id).to_a)
+ .to contain_exactly(box_horizontal_bisect_line,
+ # geographic intersects does not include box_centroid
+ box, rectangle_intersecting_box, box_rectangle_union
+ )
end
end
- context 'class methods' do
-
- specify '::geometry_sql' do
- test = 'select geom_alias_tbl.polygon::geometry from geographic_items geom_alias_tbl ' \
- 'where geom_alias_tbl.id = 2'
- expect(GeographicItem.geometry_sql(2, :polygon)).to eq(test)
- end
-
- specify '::ordered_by_shortest_distance_from to specify ordering of found objects.' do
- expect(GeographicItem).to respond_to(:ordered_by_shortest_distance_from)
+ context '::lat_long_sql' do
+ specify 'returns latitude of a point' do
+ expect(GeographicItem
+ .where(id: distant_point.id)
+ .select(GeographicItem.lat_long_sql(:latitude))
+ .first['latitude'].to_f
+ ).to be_within(0.01).of(distant_point.geo_object.lat)
end
- specify '::ordered_by_longest_distance_from' do
- expect(GeographicItem).to respond_to(:ordered_by_longest_distance_from)
- end
-
- specify '::disjoint_from to find all objects which are disjoint from an \'and\' list of objects.' do
- expect(GeographicItem).to respond_to(:disjoint_from)
+ specify 'returns longitude of a point' do
+ expect(GeographicItem
+ .where(id: distant_point.id)
+ .select(GeographicItem.lat_long_sql(:longitude))
+ .first['longitude'].to_f
+ ).to be_within(0.01).of(distant_point.geo_object.lon)
end
+ end
- specify '::within_radius_of_item to find all objects which are within a specific ' \
- 'distance of a geographic item.' do
- expect(GeographicItem).to respond_to(:within_radius_of_item)
- end
+ context '::within_radius_of_wkt_sql' do
+ before { [equator_point_long_0, equator_point_long_20,
+ equator_point_long_30].each }
- specify '::intersecting method to intersecting an \'or\' list of objects.' do
- expect(GeographicItem).to respond_to(:intersecting)
+ specify 'works for a wkt point' do
+ wkt = equator_point_long_20.to_wkt
+ r = 15 * Utilities::Geo::ONE_WEST
+ expect(GeographicItem.where(
+ GeographicItem.within_radius_of_wkt_sql(wkt, r))
+ ).to contain_exactly(equator_point_long_20, equator_point_long_30)
end
+ end
- specify '::containing_sql' do
- test1 = 'ST_Contains(polygon::geometry, (select geom_alias_tbl.point::geometry from ' \
- "geographic_items geom_alias_tbl where geom_alias_tbl.id = #{p1.id}))"
- expect(GeographicItem.containing_sql('polygon',
- p1.to_param, p1.geo_object_type)).to eq(test1)
- end
+ context '::intersecting_radius_of_wkt_sql' do
+ before { [donut, donut_bottom_interior_edge, distant_point].each }
- specify '::eval_for_type' do
- expect(GeographicItem.eval_for_type('polygon')).to eq('GeographicItem::Polygon')
- end
-
- specify '::eval_for_type' do
- expect(GeographicItem.eval_for_type('linestring')).to eq('GeographicItem::LineString')
+ specify 'works for a wkt point' do
+ wkt = donut_centroid.to_wkt
+ r = donut_centroid.st_distance_to_geographic_item(
+ donut_bottom_interior_edge
+ ) + 1
+ expect(GeographicItem.where(
+ GeographicItem.intersecting_radius_of_wkt_sql(wkt, r))
+ ).to contain_exactly(donut, donut_bottom_interior_edge, donut_centroid)
end
+ end
- specify '::eval_for_type' do
- expect(GeographicItem.eval_for_type('point')).to eq('GeographicItem::Point')
+ context '::covered_by_wkt_sql' do
+ specify 'works for a wkt multipolygon' do
+ wkt = donut_rectangle_multi_polygon.to_wkt
+ expect(GeographicItem.where(
+ GeographicItem.covered_by_wkt_sql(wkt))
+ # Should contain all donut shapes and all rectangle shapes
+ ).to contain_exactly(donut_rectangle_multi_polygon,
+ donut, donut_interior_bottom_left_multi_line,
+ donut_left_interior_edge, donut_bottom_interior_edge,
+ donut_left_interior_edge_point, rectangle_intersecting_box
+ )
end
+ end
- specify '::eval_for_type' do
- expect(GeographicItem.eval_for_type('other_thing')).to eq(nil)
+ context '::st_buffer_st_within_sql' do
+ before { [equator_point_long_0, equator_point_long_20,
+ equator_point_long_30].each }
+
+ specify 'buffer = 0, d = 0 is intersection' do
+ expect(
+ GeographicItem.where(
+ GeographicItem.st_buffer_st_within_sql(box.id, 0, 0)
+ )
+ ).to include(box, rectangle_intersecting_box)
+ end
+
+ specify 'expanding target shapes works' do
+ buffer = 15 * Utilities::Geo::ONE_WEST
+ expect(
+ GeographicItem.where(
+ GeographicItem.st_buffer_st_within_sql(
+ equator_point_long_20.id, 0, buffer
+ )
+ ).to_a
+ ).to contain_exactly(equator_point_long_20, equator_point_long_30)
+ end
+
+ specify 'shrinking target shapes works' do
+ # instantiate donut
+ donut
+
+ # control case
+ buffer = 0
+ expect(
+ GeographicItem.where(
+ GeographicItem.st_buffer_st_within_sql(
+ donut_left_interior_edge_point.id, 0, buffer)
+ ).to_a
+ ).to eq([donut])
+
+ buffer = -(1 * Utilities::Geo::ONE_WEST)
+ expect(
+ GeographicItem.where(
+ GeographicItem.st_buffer_st_within_sql(
+ donut_left_interior_edge_point.id, 0, buffer)
+ ).to_a
+ ).to eq([])
end
+ end
- context 'scopes (GeographicItems can be found by searching with) ' do
- before {
- [ce_a, ce_b, gr_a, gr_b].each
- }
-
- specify '::geo_with_collecting_event' do
- expect(GeographicItem.geo_with_collecting_event.to_a).to include(p_a, p_b) #
- end
-
- specify '::geo_with_collecting_event' do
- expect(GeographicItem.geo_with_collecting_event.to_a).not_to include(e4)
- end
-
- specify '::err_with_collecting_event' do
- expect(GeographicItem.err_with_collecting_event.to_a).to include(new_box_a, err_b) #
- end
-
- specify '::err_with_collecting_event' do
- expect(GeographicItem.err_with_collecting_event.to_a).not_to include(g, p17)
- end
-
- specify '::with_collecting_event_through_georeferences' do
- expect(GeographicItem.with_collecting_event_through_georeferences.order('id').to_a)
- .to contain_exactly(new_box_a, p_a, p_b, err_b) #
- end
-
- specify '::with_collecting_event_through_georeferences' do
- expect(GeographicItem.with_collecting_event_through_georeferences.order('id').to_a)
- .not_to include(e4)
- end
-
- specify '::include_collecting_event' do
- expect(GeographicItem.include_collecting_event.to_a)
- .to include(new_box_b, new_box_a, err_b, p_a, p_b, new_box_e)
- end
-
- context '::containing' do
- before { [k, l, b, b1, b2, e1].each }
-
- specify 'find the polygon containing the points' do
- expect(GeographicItem.containing(p1.id).to_a).to contain_exactly(k)
- end
-
- specify 'find the polygon containing all three points' do
- expect(GeographicItem.containing(p1.id, p2.id, p3.id).to_a).to contain_exactly(k)
- end
-
- specify 'find that a line string can contain a point' do
- expect(GeographicItem.containing(p4.id).to_a).to contain_exactly(l)
- end
-
- specify 'point in two polygons, but not their intersection' do
- expect(GeographicItem.containing(p18.id).to_a).to contain_exactly(b1, b2)
- end
-
- specify 'point in two polygons, one with a hole in it' do
- expect(GeographicItem.containing(p19.id).to_a).to contain_exactly(b1, b)
- end
- end
-
- context '::are_contained_in - returns objects which contained in another object.' do
- before { [e1, k].each }
-
- # OR!
- specify 'three things inside and one thing outside k' do
- expect(GeographicItem.are_contained_in_item('polygon',
- [p1, p2, p3, p11]).to_a)
- .to contain_exactly(e1, k)
- end
-
- # OR!
- specify 'one thing inside one thing, and another thing inside another thing' do
- expect(GeographicItem.are_contained_in_item('polygon',
- [p1, p11]).to_a)
- .to contain_exactly(e1, k)
- end
-
- #
- # All these are deprecated for ::containing
- #
- #
- # expect(GeographicItem.are_contained_in('not_a_column_name', @p1).to_a).to eq([])
- # expect(GeographicItem.are_contained_in('point', 'Some devious SQL string').to_a).to eq([])
-
- # specify 'one thing inside k' do
- # expect(GeographicItem.are_contained_in_item('polygon', @p1).to_a).to eq([@k])
- # end
-
- # specify 'three things inside k' do
- # expect(GeographicItem.are_contained_in_item('polygon', [@p1, @p2, @p3]).to_a).to eq([@k])
- # end
-
- # specify 'one thing outside k' do
- # expect(GeographicItem.are_contained_in_item('polygon', @p4).to_a).to eq([])
- # end
-
- # specify ' one thing inside two things (overlapping)' do
- # expect(GeographicItem.are_contained_in_item('polygon', @p12).to_a.sort).to contain_exactly(@e1, @e2)
- # end
-
- # specify 'two things inside one thing, and (1)' do
- # expect(GeographicItem.are_contained_in_item('polygon', @p18).to_a).to contain_exactly(@b1, @b2)
- # end
-
- # specify 'two things inside one thing, and (2)' do
- # expect(GeographicItem.are_contained_in_item('polygon', @p19).to_a).to contain_exactly(@b1, @b)
- # end
- end
-
- context '::contained_by' do
- before { [p1, p2, p3, p11, p12, k, l].each }
-
- specify 'find the points in a polygon' do
- expect(GeographicItem.contained_by(k.id).to_a).to contain_exactly(p1, p2, p3, k)
- end
-
- specify 'find the (overlapping) points in a polygon' do
- overlapping_point = FactoryBot.create(:geographic_item_point, point: point12.as_binary)
- expect(GeographicItem.contained_by(e1.id).to_a).to contain_exactly(p12, overlapping_point, p11, e1)
- end
- end
-
- context '::are_contained_in_item_by_id - returns objects which contained in another object.' do
- before { [p0, p1, p2, p3, p12, p13, b1, b2, b, e1, e2, k].each }
-
- specify 'one thing inside k' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p1.id).to_a).to eq([k])
- end
-
- specify 'three things inside k (in array)' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon',
- [p1.id, p2.id, p3.id]).to_a)
- .to eq([k])
- end
-
- specify 'three things inside k (as separate parameters)' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p1.id,
- p2.id,
- p3.id).to_a)
- .to eq([k])
- end
-
- specify 'one thing outside k' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p4.id).to_a)
- .to eq([])
- end
-
- specify ' one thing inside two things (overlapping)' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p12.id).to_a.sort)
- .to contain_exactly(e1, e2)
- end
-
- specify 'three things inside and one thing outside k' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon',
- [p1.id, p2.id,
- p3.id, p11.id]).to_a)
- .to contain_exactly(e1, k)
- end
-
- specify 'one thing inside one thing, and another thing inside another thing' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon',
- [p1.id, p11.id]).to_a)
- .to contain_exactly(e1, k)
- end
-
- specify 'two things inside one thing, and (1)' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p18.id).to_a)
- .to contain_exactly(b1, b2)
- end
-
- specify 'two things inside one thing, and (2)' do
- expect(GeographicItem.are_contained_in_item_by_id('polygon', p19.id).to_a)
- .to contain_exactly(b1, b)
- end
- end
-
- context '::is_contained_by - returns objects which are contained by other objects.' do
- before { [b, p0, p1, p2, p3, p11, p12, p13, p18, p19].each }
-
- specify ' three things inside k' do
- expect(GeographicItem.is_contained_by('any', k).not_including(k).to_a)
- .to contain_exactly(p1, p2, p3)
- end
-
- specify 'one thing outside k' do
- expect(GeographicItem.is_contained_by('any', p4).not_including(p4).to_a).to eq([])
- end
-
- specify 'three things inside and one thing outside k' do
- pieces = GeographicItem.is_contained_by('any',
- [e2, k]).not_including([k, e2]).to_a
- expect(pieces).to contain_exactly(p0, p1, p2, p3, p12, p13) # , @p12c
-
- end
-
- # other objects are returned as well, we just don't care about them:
- # we want to find p1 inside K, and p11 inside e1
- specify 'one specific thing inside one thing, and another specific thing inside another thing' do
- expect(GeographicItem.is_contained_by('any',
- [e1, k]).to_a)
- .to include(p1, p11)
- end
-
- specify 'one thing (p19) inside a polygon (b) with interior, and another inside ' \
- 'the interior which is NOT included (p18)' do
- expect(GeographicItem.is_contained_by('any', b).not_including(b).to_a).to eq([p19])
- end
-
- specify 'three things inside two things. Notice that the outer ring of b ' \
- 'is co-incident with b1, and thus "contained".' do
- expect(GeographicItem.is_contained_by('any',
- [b1, b2]).not_including([b1, b2]).to_a)
- .to contain_exactly(p18, p19, b)
- end
-
- # other objects are returned as well, we just don't care about them
- # we want to find p19 inside b and b1, but returned only once
- specify 'both b and b1 contain p19, which gets returned only once' do
- expect(GeographicItem.is_contained_by('any',
- [b1, b]).to_a)
- .to include(p19)
- end
- end
-
- context '::not_including([])' do
- before { [p1, p4, p17, r2024, r2022, r2020, p10].each { |object| object } }
-
- specify 'drop specifc item[s] from any scope (list of objects.)' do
- # @p2 would have been in the list, except for the exclude
- expect(GeographicItem.not_including([p2])
- .ordered_by_shortest_distance_from('point', p3)
- .limit(3).to_a)
- .to eq([p1, p4, p17])
- end
-
- specify 'drop specifc item[s] from any scope (list of objects.)' do
- # @p2 would *not* have been in the list anyway
- expect(GeographicItem.not_including([p2])
- .ordered_by_longest_distance_from('point', p3)
- .limit(3).to_a)
- .to eq([r2024, r2022, r2020])
- end
-
- specify 'drop specifc item[s] from any scope (list of objects.)' do
- # @r2022 would have been in the list, except for the exclude
- expect(GeographicItem.not_including([r2022])
- .ordered_by_longest_distance_from('point', p3)
- .limit(3).to_a)
- .to eq([r2024, r2020, p10])
- end
- end
-
- # specify '::not_including_self to drop self from any list of objects' do
- # skip 'construction of scenario'
- # expect(GeographicItem.ordered_by_shortest_distance_from('point', @p7).limit(5)).to_a).to eq([@p2, @p1, @p4])
- # end
-
- context '::ordered_by_shortest_distance_from' do
- before { [p1, p2, p4, outer_limits, l, f1, e5, e3, e4, h, rooms, f, c, g, e, j].each }
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('point', p3)
- .limit(3).to_a)
- .to eq([p2, p1, p4])
- end
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('line_string', p3)
- .limit(3).to_a)
- .to eq([outer_limits, l, f1])
- end
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('polygon', p3)
- .limit(3).to_a)
- .to eq([e5, e3, e4])
- end
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('multi_point', p3)
- .limit(3).to_a)
- .to eq([h, rooms])
- end
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('multi_line_string', p3)
- .limit(3).to_a)
- .to eq([f, c])
- end
-
- specify ' orders objects by distance from passed object' do
- subject = GeographicItem.ordered_by_shortest_distance_from('multi_polygon', p3).limit(3).to_a
- expect(subject[0..1]).to contain_exactly(new_box_e, new_box_b) # Both boxes are at same distance from p3
- expect(subject[2..]).to eq([new_box_a])
- end
-
- specify ' orders objects by distance from passed object' do
- expect(GeographicItem.ordered_by_shortest_distance_from('geometry_collection', p3)
- .limit(3).to_a)
- .to eq([e, j])
- end
- end
-
- context '::ordered_by_longest_distance_from' do
- before {
- [r2024, r2022, r2020, c3, c1, c2, g1, g2, g3, b2, rooms, h, c, f, g, j, e].each
- }
-
- specify 'orders points by distance from passed point' do
- expect(GeographicItem.ordered_by_longest_distance_from('point', p3).limit(3).to_a)
- .to eq([r2024, r2022, r2020])
- end
-
- specify 'orders line_strings by distance from passed point' do
- expect(GeographicItem.ordered_by_longest_distance_from('line_string', p3)
- .limit(3).to_a)
- .to eq([c3, c1, c2])
- end
-
- specify 'orders polygons by distance from passed point' do
- expect(GeographicItem.ordered_by_longest_distance_from('polygon', p3)
- .limit(4).to_a)
- .to eq([g1, g2, g3, b2])
- end
-
- specify 'orders multi_points by distance from passed point' do
- expect(GeographicItem.ordered_by_longest_distance_from('multi_point', p3)
- .limit(3).to_a)
- .to eq([rooms, h])
- end
-
- specify 'orders multi_line_strings by distance from passed point' do
- expect(GeographicItem.ordered_by_longest_distance_from('multi_line_string', p3)
- .limit(3).to_a)
- .to eq([c, f])
- end
-
- specify 'orders multi_polygons by distance from passed point' do
- # existing multi_polygons: [new_box_e, new_box_a, new_box_b, g]
- # new_box_e is excluded, because p3 is *exactly* the same distance from new_box_e, *and* new_box_a
- # This seems to be the reason these two objects *might* be in either order. Thus, one of the two
- # is excluded to prevent it from confusing the order (farthest first) of the appearance of the objects.
- expect(GeographicItem.ordered_by_longest_distance_from('multi_polygon', p3)
- .not_including(new_box_e)
- .limit(3).to_a) # TODO: Limit is being called over an array. Check whether this is a gem/rails bug or we need to update code.
- .to eq([g, new_box_a, new_box_b])
- end
-
- specify 'orders objects by distance from passed object geometry_collection' do
- expect(GeographicItem.ordered_by_longest_distance_from('geometry_collection', p3)
- .limit(3).to_a)
- .to eq([j, e])
- end
- end
-
- context '::disjoint_from' do
- before { [p1].each }
-
- specify "list of objects (uses 'and')." do
- expect(GeographicItem.disjoint_from('point',
- [e1, e2, e3, e4, e5])
- .order(:id)
- .limit(1).to_a)
- .to contain_exactly(p_b)
- end
- end
-
- context '::within_radius_of_item' do
- before { [e2, e3, e4, e5, item_a, item_b, item_c, item_d, k, r2022, r2024, p14].each }
-
- specify 'returns objects within a specific distance of an object.' do
- pieces = GeographicItem.within_radius_of_item(p0.id, 1000000)
- .where(type: ['GeographicItem::Polygon'])
- expect(pieces).to contain_exactly(err_b, e2, e3, e4, e5, item_a, item_b, item_c, item_d)
- end
-
- specify '::within_radius_of_item("any", ...)' do
- expect(GeographicItem.within_radius_of_item(p0.id, 1000000))
- .to include(e2, e3, e4, e5, item_a, item_b, item_c, item_d)
- end
-
- specify "::intersecting list of objects (uses 'or')" do
- expect(GeographicItem.intersecting('polygon', [l])).to eq([k])
- end
-
- specify "::intersecting list of objects (uses 'or')" do
- expect(GeographicItem.intersecting('polygon', [f1]))
- .to eq([]) # Is this right?
- end
-
- specify '::select_distance_with_geo_object provides an extra column called ' \
- '\'distance\' to the output objects' do
- result = GeographicItem.select_distance_with_geo_object('point', r2020)
- .limit(3).order('distance')
- .where_distance_greater_than_zero('point', r2020).to_a
- # get back these three points
- expect(result).to eq([r2022, r2024, p14])
- end
-
- specify '::select_distance_with_geo_object provides an extra column called ' \
- '\'distance\' to the output objects' do
- result = GeographicItem.select_distance_with_geo_object('point', r2020)
- .limit(3).order('distance')
- .where_distance_greater_than_zero('point', r2020).to_a
- # 5 meters
- expect(result.first.distance).to be_within(0.1).of(5.008268179)
- end
-
- specify '::select_distance_with_geo_object provides an extra column called ' \
- '\'distance\' to the output objects' do
- result = GeographicItem.select_distance_with_geo_object('point', r2020)
- .limit(3).order('distance')
- .where_distance_greater_than_zero('point', r2020).to_a
- # 10 meters
- expect(result[1].distance).to be_within(0.1).of(10.016536381)
- end
-
- specify '::select_distance_with_geo_object provides an extra column called ' \
- '\'distance\' to the output objects' do
- result = GeographicItem.select_distance_with_geo_object('point', r2020)
- .limit(3).order('distance')
- .where_distance_greater_than_zero('point', r2020).to_a
- # 5,862 km (3,642 miles)
- expect(result[2].distance).to be_within(0.1).of(5862006.0029975)
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(p0)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(a)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(b)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(h)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(f)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(g)).to be_truthy
- end
-
- specify '::with_is_valid_geometry_column returns \'true\' for a valid GeoItem' do
- expect(GeographicItem.with_is_valid_geometry_column(all_items)).to be_truthy
- end
- end
-
- context 'distance to others' do
- specify 'slow' do
- expect(p1.st_distance(p2.id)).to be_within(0.1).of(497835.8972059313)
- end
+ context '#circle' do
+ let(:origin) { Gis::FACTORY.point(0, 0) }
+ specify 'buffer resolution determines # of sides of polygon' do
+ expect(
+ GeographicItem.circle(origin, 10, 3).exterior_ring.points.count
+ ).to eq(13)
- specify 'fast' do
- expect(p1.st_distance_spheroid(p2.id)).to be_within(0.1).of(479988.253998808)
- end
- end
+ expect(
+ GeographicItem.circle(origin, 10, 10).exterior_ring.points.count
+ ).to eq(41)
end
- context '::gather_geographic_area_or_shape_data' do
- specify 'collection_objetcs' do
+ specify 'has approximate expected shape' do
+ c = GeographicItem.circle(origin, 10)
+ # Rough estimates - the input unit to buffer here is "degrees" since
+ # our factory projection is WGS84
+ # (buffer is 2d so all z-coordinates are NaN, but that seems to be okay
+ # for our purposes)
+ smaller_circle = origin.buffer(9.5 / Utilities::Geo::ONE_WEST_MEAN)
+ larger_circle = origin.buffer(10.5 / Utilities::Geo::ONE_WEST_MEAN)
- end
- specify 'asserted_distribution' do
-
- end
+ expect(c.contains?(smaller_circle)).to be true
+ expect(larger_circle.contains?(c)).to be true
end
end
- end # end using ce_test_objects
-
- context 'concerns' do
- it_behaves_like 'is_data'
- end
+ end
- end
+end
diff --git a/spec/models/georeference_spec.rb b/spec/models/georeference_spec.rb
index f114cbc00a..75558c2056 100644
--- a/spec/models/georeference_spec.rb
+++ b/spec/models/georeference_spec.rb
@@ -8,8 +8,8 @@
let(:earth) { FactoryBot.create(:earth_geographic_area) }
let(:g_a_t) { FactoryBot.create(:testbox_geographic_area_type) }
- let(:e_g_i) { GeographicItem.create!(polygon: box_1) }
- let(:item_d) { GeographicItem.create!(polygon: box_4) }
+ let(:e_g_i) { GeographicItem.create!(geography: box_1) }
+ let(:item_d) { GeographicItem.create!(geography: box_4) }
let!(:g_a) { GeographicArea.create!(
name: 'Box_4',
@@ -136,7 +136,7 @@
specify 'is rounded to nearest meter' do
georeference.error_radius = 1.23
georeference.valid?
- expect(georeference.error_radius).to eq(1)
+ expect(georeference.error_radius).to eq(1)
end
end
@@ -161,19 +161,19 @@
end
specify 'geographic_item without error_radius, error_depth, or error_geographic_item' do
- georeference.geographic_item = GeographicItem.new(point: simple_shapes[:point])
+ georeference.geographic_item = GeographicItem.new(geography: simple_shapes[:point])
expect(georeference.valid?).to be_truthy
end
specify 'geographic_item with error_radius' do
- georeference.geographic_item = GeographicItem.new(point: simple_shapes[:point])
+ georeference.geographic_item = GeographicItem.new(geography: simple_shapes[:point])
georeference.error_radius = 10000
expect(georeference.valid?).to be_truthy
end
specify 'geographic_item with error_geographic_item' do
- georeference.geographic_item = GeographicItem.new(point: 'POINT(5 5 0)')
- georeference.geographic_item = GeographicItem.new(polygon: 'POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, ' \
+ georeference.geographic_item = GeographicItem.new(geography: 'POINT(5 5 0)')
+ georeference.geographic_item = GeographicItem.new(geography: 'POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, ' \
'10.0 10.0 0.0, 0.0 10.0 0.0, 0.0 0.0 0.0))')
expect(georeference.valid?).to be_truthy
end
@@ -188,7 +188,7 @@
specify 'errors which result from badly formed error_geographic_item values' do
g = Georeference::VerbatimData.new(
collecting_event: collecting_event_with_geographic_area,
- error_geographic_item: GeographicItem.new(polygon: poly_e1))
+ error_geographic_item: GeographicItem.new(geography: poly_e1))
g.valid?
expect(g.errors[:error_geographic_item]).to be_present
expect(g.errors[:collecting_event]).to be_present
@@ -207,11 +207,11 @@
end
context 'testing geographic_item/error against geographic area provided through collecting event' do
- let(:p0) { GeographicItem.new(point: point0) }
- let(:p18) { GeographicItem.new(point: point18) }
- let(:gi_b1) { GeographicItem.create!(polygon: shape_b_outer) }
- let(:gi_b2) { GeographicItem.create!(polygon: shape_b_inner) }
- let(:gi_e1) { GeographicItem.create!(polygon: poly_e1) }
+ let(:p0) { GeographicItem.new(geography: point0) }
+ let(:p18) { GeographicItem.new(geography: point18) }
+ let(:gi_b1) { GeographicItem.create!(geography: shape_b_outer) }
+ let(:gi_b2) { GeographicItem.create!(geography: shape_b_inner) }
+ let(:gi_e1) { GeographicItem.create!(geography: poly_e1) }
let(:ga_e1) { GeographicArea.create!(
name: 'test area E1',
data_origin: 'Test Data',
@@ -264,7 +264,7 @@
specify 'If point is within 10km from collecting event''s geographic area georeference is valid' do
g = Georeference::VerbatimData.new(
collecting_event: ce_e1, # p0 is outside of both e_g_i and ce.geographic_area
- geographic_item: GeographicItem.new(point: RSPEC_GEO_FACTORY.point(-9.0, 9.0904, 0.0)) # e_g_i is test_box_1
+ geographic_item: GeographicItem.new(geography: RSPEC_GEO_FACTORY.point(-9.0, 9.0904, 0.0)) # e_g_i is test_box_1
)
expect(g).to be_valid
end
@@ -272,7 +272,7 @@
specify 'If point is exceeds 10km from collecting event''s geographic area georeference is valid' do
g = Georeference::VerbatimData.new(
collecting_event: ce_e1, # p0 is outside of both e_g_i and ce.geographic_area
- geographic_item: GeographicItem.new(point: RSPEC_GEO_FACTORY.point(-9.0, 9.0907, 0.0)) # e_g_i is test_box_1
+ geographic_item: GeographicItem.new(geography: RSPEC_GEO_FACTORY.point(-9.0, 9.0907, 0.0)) # e_g_i is test_box_1
)
expect(g).to_not be_valid
end
@@ -378,7 +378,7 @@
let(:gr1) { FactoryBot.create(
:valid_georeference,
collecting_event: FactoryBot.create(:valid_collecting_event),
- geographic_item: FactoryBot.create(:geographic_item_with_polygon, polygon: shape_k)
+ geographic_item: FactoryBot.create(:geographic_item, geography: shape_k)
) }
let(:gr_poly) { FactoryBot.create(:valid_georeference_geo_locate) }
diff --git a/spec/routing/gazetteers_routing_spec.rb b/spec/routing/gazetteers_routing_spec.rb
new file mode 100644
index 0000000000..5e0c42bfa6
--- /dev/null
+++ b/spec/routing/gazetteers_routing_spec.rb
@@ -0,0 +1,38 @@
+require "rails_helper"
+
+RSpec.describe GazetteersController, type: :routing do
+ describe "routing" do
+ it "routes to #index" do
+ expect(get: "/gazetteers").to route_to("gazetteers#index")
+ end
+
+ it "routes to #new" do
+ expect(get: "/gazetteers/new").to route_to("gazetteers#new")
+ end
+
+ it "routes to #show" do
+ expect(get: "/gazetteers/1").to route_to("gazetteers#show", id: "1")
+ end
+
+ it "routes to #edit" do
+ expect(get: "/gazetteers/1/edit").to route_to("gazetteers#edit", id: "1")
+ end
+
+
+ it "routes to #create" do
+ expect(post: "/gazetteers").to route_to("gazetteers#create")
+ end
+
+ it "routes to #update via PUT" do
+ expect(put: "/gazetteers/1").to route_to("gazetteers#update", id: "1")
+ end
+
+ it "routes to #update via PATCH" do
+ expect(patch: "/gazetteers/1").to route_to("gazetteers#update", id: "1")
+ end
+
+ it "routes to #destroy" do
+ expect(delete: "/gazetteers/1").to route_to("gazetteers#destroy", id: "1")
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index b87390825b..fe2c4db301 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,3 +1,4 @@
+#ActiveRecord::Base.logger = Logger.new(STDOUT)
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
diff --git a/spec/support/shared_contexts/shared_basic_geo.rb b/spec/support/shared_contexts/shared_basic_geo.rb
new file mode 100644
index 0000000000..9b09b00cb5
--- /dev/null
+++ b/spec/support/shared_contexts/shared_basic_geo.rb
@@ -0,0 +1,351 @@
+require 'support/vendor/rspec_geo_helpers'
+
+# See shared_geo.rb for a more expansive variety of shapes
+
+shared_context 'stuff for GeographicItem tests' do
+
+ ###### Simple shapes - no intended relation to each other
+ let(:simple_shapes) { {
+ point: 'POINT(10 -10 0)',
+ line_string: 'LINESTRING(0.0 0.0 0.0, 10.0 0.0 0.0)',
+ polygon:'POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, 0.0 0.0 0.0))',
+ multi_point: 'MULTIPOINT((10.0 10.0 0.0), (20.0 20.0 0.0))',
+ multi_line_string: 'MULTILINESTRING((0.0 0.0 0.0, 10.0 0.0 0.0), (20.0 0.0 0.0, 30.0 0.0 0.0))',
+ multi_polygon: 'MULTIPOLYGON(((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, ' \
+ '0.0 0.0 0.0)),((10.0 10.0 0.0, 20.0 10.0 0.0, 20.0 20.0 0.0, 10.0 20.0 0.0, 10.0 10.0 0.0)))',
+ geometry_collection: 'GEOMETRYCOLLECTION( POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, ' \
+ '0.0 10.0 0.0, 0.0 0.0 0.0)), POINT(10 10 0)) ',
+ geography:'POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, 0.0 0.0 0.0))'
+ }.freeze }
+
+ let(:simple_point) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:point]
+ )
+ }
+
+ let(:simple_line_string) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:line_string]
+ )
+ }
+
+ let(:simple_polygon) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:polygon]
+ )
+ }
+
+ let(:simple_multi_point) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:multi_point]
+ )
+ }
+
+ let(:simple_multi_line_string) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:multi_line_string]
+ )
+ }
+
+ let(:simple_multi_polygon) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:multi_polygon]
+ )
+ }
+
+ let(:simple_geometry_collection) {
+ FactoryBot.create(
+ :geographic_item, geography: simple_shapes[:geometry_collection]
+ )
+ }
+
+ ###### Specific shapes for testing spatial relations between shapes
+ # !! Specs should make no assumptions about the size of these shapes or
+ # about their location in space; for that see distance spec shapes below
+ #
+ # donut box distant_point
+ # --------- --------- #
+ # | | | |
+ # | &---- | | %%%%%%%%%
+ # | & | | | % | %
+ # | # # | | &&&&&&& %
+ # | & | | | % | % rectangle_intersecting_box
+ # | &&&&& | | % # | %
+ # |# | | % | %
+ # --------- ----%%%%%%%%%
+ #
+ # # = point, & = line
+ #
+ # Donut shapes: donut, donut_left_interior_edge, donut_bottom_interior_edge
+ # donut_interior_bottom_left_multi_line,
+ # donut_centroid, donut_left_interior_edge_point, donut_interior_point
+ #
+ # Box shapes: box, box_horizontal_bisect_line, box_centroid
+ #
+ # Rectangle shapes: rectangle_intersecting_box,
+ # box_rectangle_intersection_point
+ #
+ # box_rectangle_union is what it says as a single polygon
+ #
+ # !! Note that multi-shapes instantiate their constituent shapes
+ # MultiPoint: donut_box_multi_point (donut_interior_point, box_centroid)
+ # MultiLine: donut_interior_bottom_left_multi_line
+ # MultiPolygon: donut_rectangle_multi_polygon
+ # GeometryCollection: donut_box_bisector_rectangle_geometry_collection
+ # (donut, box_horizontal_bisect_line, rectangle_intersecting_box)
+
+ ### Point intended to be outside of any of the shapes defined below
+ let(:distant_point) {
+ FactoryBot.create(:geographic_item, geography: 'POINT(170 80 0)')
+ }
+
+ ### A donut polygon and sub-shapes
+ # donut = POLYGON((0 0 0, 20 0 0, 20 20 0, 0 20 0, 0 0 0),
+ # (5 5 0, 15 5 0, 15 15 0, 5 15 0, 5 5 0))
+ # !! we describe donut outside of the definition of donut so that we can also
+ # describe its subshapes without instantiating donut in doing so
+ let(:d_llc_x) { 0 }
+ let(:d_llc_y) { 0 }
+ # width == height, but that's not necessary
+ let(:d_w) { 20 } # should be divisible by 4
+ let(:d_h) { 20 } # should be divisible by 4
+ # width of the ring == height of the ring, but that's not necessary
+ let(:d_ring_w) { 5 }
+ let(:d_ring_h) { 5 }
+ # For convenience:
+ # 'or' == outer ring
+ let(:d_or_llc) { '0 0 0' }
+ let(:d_or_lrc) { '20 0 0' }
+ let(:d_or_urc) { '20 20 0' }
+ let(:d_or_ulc) { '0 20 0' }
+ # 'ir' == inner ring
+ let(:d_ir_llc) { '5 5 0' }
+ let(:d_ir_ulc) { '15 5 0' }
+ let(:d_ir_urc) { '15 15 0' }
+ let(:d_ir_lrc) { '5 15 0' }
+
+ let(:donut) do
+ p = "POLYGON((#{d_or_llc}, #{d_or_lrc}, #{d_or_urc}, #{d_or_ulc},
+ #{d_or_llc}),
+ (#{d_ir_llc}, #{d_ir_ulc}, #{d_ir_urc}, #{d_ir_lrc},
+ #{d_ir_llc}))"
+
+ FactoryBot.create(:geographic_item, geography: p)
+ end
+
+ # geometric centroid
+ let(:donut_centroid) do
+ c_x = d_llc_x + d_w / 2
+ c_y = d_llc_y + d_h / 2
+ c = "POINT (#{c_x} #{c_y} 0)"
+
+ FactoryBot.create(:geographic_item, geography: c)
+ end
+
+ let (:donut_interior_point) do
+ interior_x = d_llc_x + d_ring_w / 2
+ interior_y = d_llc_y + d_ring_h / 2
+ i_p = "POINT(#{interior_x} #{interior_y} 0)"
+
+ FactoryBot.create(:geographic_item, geography: i_p)
+ end
+
+ let (:donut_left_interior_edge_point) {
+ p_x = d_llc_x + d_ring_w
+ p_y = d_llc_y + d_h / 2
+ p = "POINT (#{p_x} #{p_y} 0)"
+
+ FactoryBot.create(:geographic_item, geography: p)
+ }
+
+ let (:donut_left_interior_edge) {
+ l_x = d_llc_x + d_ring_w
+ l_lower_y = d_llc_y + d_ring_h
+ l_upper_y = d_llc_y + d_h - d_ring_h
+ l = "LINESTRING (#{l_x} #{l_lower_y}, #{l_x} #{l_upper_y})"
+
+ FactoryBot.create(:geographic_item, geography: l)
+ }
+
+ let (:donut_bottom_interior_edge) {
+ l_left_x = d_llc_x + d_ring_w
+ l_right_x = d_llc_x + d_w - d_ring_w
+ l_y = d_llc_y + d_ring_h
+ l = "LINESTRING (#{l_left_x} #{l_y}, #{l_right_x} #{l_y})"
+
+ FactoryBot.create(:geographic_item, geography: l)
+ }
+
+ # A multi_line_string
+ let (:donut_interior_bottom_left_multi_line) {
+ m_l = RSPEC_GEO_FACTORY.multi_line_string([
+ donut_left_interior_edge.geo_object,
+ donut_bottom_interior_edge.geo_object
+ ])
+
+ FactoryBot.create(:geographic_item, geography: m_l)
+ }
+
+ ### A box polygon and sub-shapes
+
+ # box = POLYGON((40 0 0, 60 0 0, 60 20 0, 40 20 0, 40 0 0))
+ let(:box_llc_x) {40}
+ let(:box_llc_y) {0}
+ # width == height, though that isn't necessary
+ let(:box_w) {20} # should be divisible by 2
+ let(:box_h) {20} # should be divisible by 2
+ # For convenience:
+ let(:box_llc) {'40 0 0'}
+ let(:box_lrc) {'60 0 0'}
+ let(:box_urc) {'60 20 0'}
+ let(:box_ulc) {'40 20 0'}
+
+
+ let(:box) do
+ b = "POLYGON((#{box_llc}, #{box_lrc}, #{box_urc}, #{box_ulc}, #{box_llc}))"
+
+ FactoryBot.create(:geographic_item, geography: b)
+ end
+
+ # geometric centroid - *not* covered by box_horizontal_bisect_line in
+ # geographic coordinates
+ let(:box_centroid) {
+ c_x = box_llc_x + box_w / 2
+ c_y = box_llc_y + box_h / 2
+ c = "POINT (#{c_x} #{c_y} 0)"
+
+ FactoryBot.create(:geographic_item, geography: c)
+ }
+
+ let(:box_horizontal_bisect_line) {
+ left_x = box_llc_x
+ right_x = left_x + box_w
+ y = box_llc_y + box_h / 2
+ line = "LINESTRING (#{left_x} #{y} 0, #{right_x} #{y} 0)"
+ FactoryBot.create(:geographic_item, geography: line)
+ }
+
+ ### A rectangle polygon intersecting the previous box; both start at the same
+ # height, the rectangle is taller than box_centroid but shorter than box (is
+ # that important?); box_centroid is in the left side of rectangle; the right
+ # side of rectangle is beyond the right side of box
+
+ # rectangle = POLYGON((50 0 0, 70 0 0, 70 15 0, 50 15 0, 50 0 0))
+ let(:rect_w) { box_w }
+ let(:rect_h) { 3 * box_h / 4 }
+ let(:rectangle_intersecting_box) do
+ llc_x = box_llc_x + box_w / 2
+ llc_y = box_llc_y
+
+ lrc_x = llc_x + rect_w
+ lrc_y = llc_y
+
+ urc_x = lrc_x
+ urc_y = lrc_y + rect_h
+
+ ulc_x = llc_x
+ ulc_y = urc_y
+
+ r = "POLYGON ((#{llc_x} #{llc_y} 0, #{lrc_x} #{lrc_y} 0, " \
+ "#{urc_x} #{urc_y} 0, #{ulc_x} #{ulc_y} 0, " \
+ "#{llc_x} #{llc_y} 0))"
+
+ FactoryBot.create(:geographic_item, geography: r)
+ end
+
+ ### A point in the interior of the intersection of box and rectangle
+ let(:box_rectangle_intersection_point) {
+ i_x = box_llc_x + 3 * box_w / 4
+ i_y = box_llc_y + box_h / 4
+ i = "POINT (#{i_x} #{i_y})"
+
+ FactoryBot.create(:geographic_item, geography: i)
+ }
+
+ ### The union of the box and rectangle polygons as a single polygon
+ let(:box_rectangle_union) {
+ m_p = box.geo_object.union(rectangle_intersecting_box.geo_object)
+
+ FactoryBot.create(:geographic_item, geography: m_p)}
+
+ ###### Multi-shapes
+ # !! Note these instantiate their constituent shapes
+
+ ### A multi_point
+ let(:donut_box_multi_point) do
+ donut_point = donut_interior_point.geo_object
+ box_point = box_centroid.geo_object
+ m_p = RSPEC_GEO_FACTORY.multi_point([donut_point, box_point])
+
+ FactoryBot.create(:geographic_item, geography: m_p)
+ end
+
+ ### A mult_line_string
+ # :donut_interior_bottom_left_multi_line is a multi_line_string
+
+ ### A multi_polygon
+ let(:donut_rectangle_multi_polygon) do
+ m_poly = RSPEC_GEO_FACTORY.multi_polygon(
+ [donut.geo_object, rectangle_intersecting_box.geo_object]
+ )
+
+ FactoryBot.create(:geographic_item, geography: m_poly)
+ end
+
+ ### A geometry_collection
+ let(:donut_box_bisector_rectangle_geometry_collection) do
+ g_c = RSPEC_GEO_FACTORY.collection(
+ [
+ donut.geo_object,
+ rectangle_intersecting_box.geo_object,
+ box_horizontal_bisect_line.geo_object
+ ]
+ )
+
+ FactoryBot.create(:geographic_item, geography: g_c)
+ end
+
+ ###### Shapes used for specs involving distances
+ # These are all along the equator where it's easier to compute distances
+ # !! These bear no explicit reference to the other shapes above, but be
+ # aware that if you include shapes above with these, your distance specs
+ # could capture those shapes as well
+ let(:equator_point_long_0) {
+ FactoryBot.create(:geographic_item, geography: 'POINT (0 0)')
+ }
+
+ let(:equator_point_long_20) {
+ FactoryBot.create(:geographic_item, geography: 'POINT (20 0)')
+ }
+
+ let(:equator_point_long_30) {
+ FactoryBot.create(:geographic_item, geography: 'POINT (30 0)')
+ }
+
+ ###### Collecting events
+ let(:ce_box_centroid) {
+ FactoryBot.create(:valid_collecting_event)
+ }
+
+ let(:ce_rectangle_point) {
+ FactoryBot.create(:valid_collecting_event)
+ }
+
+ ###### Georeferences
+ let(:gr_box_centroid) {
+ FactoryBot.create(:georeference_verbatim_data,
+ api_request: 'gr_box',
+ collecting_event: ce_box_centroid,
+ geographic_item: box_centroid,
+ error_geographic_item: box)
+ }
+
+ let(:gr_rectangle_point) {
+ FactoryBot.create(:georeference_verbatim_data,
+ api_request: 'gr_rectangle_point',
+ collecting_event: ce_rectangle_point,
+ geographic_item: box_rectangle_intersection_point,
+ error_geographic_item: rectangle_intersecting_box)
+ }
+end
diff --git a/spec/support/shared_contexts/shared_cached_map.rb b/spec/support/shared_contexts/shared_cached_map.rb
index ab2de12532..0e6f4c90d8 100644
--- a/spec/support/shared_contexts/shared_cached_map.rb
+++ b/spec/support/shared_contexts/shared_cached_map.rb
@@ -41,9 +41,9 @@
# let(:point_in) { RSPEC_GEO_FACTORY.point(5, 5, 0.0) }
# let(:point_out) { RSPEC_GEO_FACTORY.point(20, 20, 0.0) }
- let(:gi1) { GeographicItem.create(multi_polygon: g1)}
- let(:gi2) { GeographicItem.create(polygon: g2)}
- let(:gi3) { GeographicItem.create(polygon: g3)}
+ let(:gi1) { GeographicItem.create(geography: g1)}
+ let(:gi2) { GeographicItem.create(geography: g2)}
+ let(:gi3) { GeographicItem.create(geography: g3)}
let(:geographic_area_type) { GeographicAreaType.create!(name: 'Country') }
diff --git a/spec/support/shared_contexts/shared_geo.rb b/spec/support/shared_contexts/shared_geo.rb
index 86f2e49c3b..99df14438f 100644
--- a/spec/support/shared_contexts/shared_geo.rb
+++ b/spec/support/shared_contexts/shared_geo.rb
@@ -13,138 +13,6 @@
@project.nil? ? Project.find(project_id) : @project
}
- let(:json_string) { '{"type":"Feature", "properties":{}, "geometry":{"type":"MultiPolygon", ' \
- '"coordinates":[[[[0, 10, 0], [10, 10, 0], [10, -10, 0], [0, -10, 0], [0, 10, 0]]]]}}' }
-
- # A simple world in a complex set of data.
- #
- # Our simple world is used to X.
- # It contains the following:
- # TODO: enumerate
- # * A
- # * B
- # * C
- #
- # To use elements of the world invoke them in a begin block:
- # begin do
- # co_a
- # co_b
- # end
- #
-
- let(:geo_root_taxon_name) { Protonym.create!(name: 'Root', parent: nil, rank_class: NomenclaturalRank, by: geo_user, project: geo_project) }
- let(:geo_species) { Protonym.create!(name: 'antivitis', parent: geo_root_taxon_name , rank_class: Ranks.lookup(:iczn, :species), by: geo_user, project: geo_project) }
-
- let(:geo_family1) { Protonym.create!(
- name: 'Topdogidae',
- parent: geo_root_taxon_name,
- rank_class: Ranks.lookup(:iczn, :family),
- by: geo_user, project: geo_project,
- taxon_name_author_roles_attributes: [{person: ted, by: geo_user, project: geo_project}])
- }
-
- let(:geo_family2) { Protonym.create!(
- name: 'Nutherdogidae',
- parent: geo_root_taxon_name,
- rank_class: Ranks.lookup(:iczn, :family),
- by: geo_user,
- project: geo_project,
- taxon_name_author_roles_attributes: [
- {person: bill, by: geo_user, project: geo_project},
- {person: ted, by: geo_user, project: geo_project},
- {person: sargon, by: geo_user, project: geo_project}])
- }
-
- let(:tn_abra) { Protonym.create!(
- name: 'Abra',
- rank_class: Ranks.lookup(:iczn, :genus),
- parent: geo_family1,
- by: geo_user,
- project: geo_project,
- taxon_name_author_roles_attributes: [{person: ted, by: geo_user, project: geo_project}])
- }
-
- let(:tn_spooler) { Protonym.create!(
- name: 'spooler',
- rank_class: Ranks.lookup(:iczn, :species),
- parent: tn_abra,
- by: geo_user,
- project: geo_project,
- taxon_name_author_roles_attributes: [{person: sargon, by: geo_user, project: geo_project}, {person: daryl, by: geo_user, project: geo_project}])
- }
-
- let(:tn_cadabra) { Protonym.create!(
- name:'cadabra',
- year_of_publication: 2017,
- verbatim_author: 'Bill Ardson',
- rank_class: Ranks.lookup(:iczn, :species),
- parent: tn_abra,
- by: geo_user,
- project: geo_project,
- taxon_name_author_roles_attributes: [{person: bill, by: geo_user, project: geo_project}])
- }
-
- let(:tn_alakazam) { Protonym.create!(
- name: 'alakazam',
- rank_class: Ranks.lookup(:iczn, :subspecies),
- parent: tn_cadabra,
- by: geo_user,
- project: geo_project,
- taxon_name_author_roles_attributes: [ {person: ted, by: geo_user, project: geo_project}]) # {person: bill, by: geo_user, project: geo_project},
- }
-
- # TODO: candidate for removal?
- # no authors!
- let(:tn_beevitis) { Protonym.create!(
- name: 'beevitis',
- rank_class: Ranks.lookup(:iczn, :species),
- parent: geo_root_taxon_name,
- by: geo_user,
- project: geo_project)
- }
-
- # Otus
- let(:abra) {
- Otu.create!(
- name: 'Abra',
- taxon_name: tn_abra,
- by: geo_user,
- project: geo_project)
- }
-
- let(:cadabra) { Otu.create!(
- name: 'Abra cadabra',
- taxon_name: tn_cadabra,
- by: geo_user,
- project: geo_project)
- }
-
- let(:alakazam) { Otu.create!(
- name: 'Abra cadabra alakazam',
- taxon_name: tn_alakazam,
- project: geo_project,
- by: geo_user)
- }
-
- let(:spooler) {
- Otu.create!(
- name: "Sargon's spooler",
- project: geo_project,
- taxon_name: tn_spooler,
- by: geo_user)
- }
-
- let(:otu_p4) {
- Otu.create!(
- name: 'P4',
- taxon_name: tn_beevitis,
- by: geo_user,
- project: geo_project)
- }
-
- #
- # Somehwere around here simple world ends
- #
let(:simple_shapes) { {
point: 'POINT(10 10 0)',
@@ -155,130 +23,14 @@
multi_polygon: 'MULTIPOLYGON(((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, ' \
'0.0 0.0 0.0)),((10.0 10.0 0.0, 20.0 10.0 0.0, 20.0 20.0 0.0, 10.0 20.0 0.0, 10.0 10.0 0.0)))',
geometry_collection: 'GEOMETRYCOLLECTION( POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, ' \
- '0.0 10.0 0.0, 0.0 0.0 0.0)), POINT(10 10 0)) '
+ '0.0 10.0 0.0, 0.0 0.0 0.0)), POINT(10 10 0)) ',
+ geography:'POLYGON((0.0 0.0 0.0, 10.0 0.0 0.0, 10.0 10.0 0.0, 0.0 10.0 0.0, 0.0 0.0 0.0))'
}.freeze }
- let(:room2024) { RSPEC_GEO_FACTORY.point(-88.241413, 40.091655, 757) }
- let(:room2020) { RSPEC_GEO_FACTORY.point(-88.241421, 40.091565, 757) }
- let(:room2022) { RSPEC_GEO_FACTORY.point((room2020.x + ((room2024.x - room2020.x) / 2)),
- (room2020.y + ((room2024.y - room2020.y) / 2)),
- (room2020.z + ((room2024.z - room2020.z) / 2))) }
-
- let(:rooms20nn) { RSPEC_GEO_FACTORY.multi_point(
- [room2020,
- room2022,
- room2024])}
-
- let(:gi_point_a) { RSPEC_GEO_FACTORY.point(-88.241413, 40.091655, 0.0) }
- let(:gi_point_c) { RSPEC_GEO_FACTORY.point(-88.243386, 40.116402, 0.0) }
- let(:gi_point_m) { RSPEC_GEO_FACTORY.point(-88.196736, 40.090091, 0.0) }
- let(:gi_point_u) { RSPEC_GEO_FACTORY.point(-88.204517, 40.110037, 0.0) }
- let(:gi_ls01) { RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-32, 21, 0.0),
- RSPEC_GEO_FACTORY.point(-25, 21, 0.0),
- RSPEC_GEO_FACTORY.point(-25, 16, 0.0),
- RSPEC_GEO_FACTORY.point(-21, 20, 0.0)]) }
- let(:gi_ls02) { RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-32, 21, 0.0),
- RSPEC_GEO_FACTORY.point(-25, 21, 0.0),
- RSPEC_GEO_FACTORY.point(-25, 16, 0.0),
- RSPEC_GEO_FACTORY.point(-21, 20, 0.0)]) }
- let(:gi_polygon) { RSPEC_GEO_FACTORY.polygon(gi_ls02) }
- let(:gi_multi_polygon) { RSPEC_GEO_FACTORY.multi_polygon(
- [RSPEC_GEO_FACTORY.polygon(
- RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-168.16047115799995, -14.520928643999923, 0.0),
- RSPEC_GEO_FACTORY.point(-168.16156979099992, -14.532891533999944, 0.0),
- RSPEC_GEO_FACTORY.point(-168.17308508999994, -14.523695570999877, 0.0),
- RSPEC_GEO_FACTORY.point(-168.16352291599995, -14.519789320999891, 0.0),
- RSPEC_GEO_FACTORY.point(-168.16047115799995, -14.520928643999923, 0.0)]
- )
- ),
-
- RSPEC_GEO_FACTORY.polygon(
- RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-170.62006588399993, -14.254571221999868, 0.0),
- RSPEC_GEO_FACTORY.point(-170.59101314999987, -14.264825127999885, 0.0),
- RSPEC_GEO_FACTORY.point(-170.5762426419999, -14.252536716999927, 0.0),
- RSPEC_GEO_FACTORY.point(-170.5672501289999, -14.258558851999851, 0.0),
- RSPEC_GEO_FACTORY.point(-170.5684708319999, -14.27092864399988, 0.0),
- RSPEC_GEO_FACTORY.point(-170.58417721299995, -14.2777645809999, 0.0),
- RSPEC_GEO_FACTORY.point(-170.6423233709999, -14.280694268999909, 0.0),
- RSPEC_GEO_FACTORY.point(-170.65929114499988, -14.28525155999995, 0.0),
- RSPEC_GEO_FACTORY.point(-170.68358313699994, -14.302829684999892, 0.0),
- RSPEC_GEO_FACTORY.point(-170.7217911449999, -14.353448174999883, 0.0),
- RSPEC_GEO_FACTORY.point(-170.74864661399988, -14.374688408999873, 0.0),
- RSPEC_GEO_FACTORY.point(-170.75548255099991, -14.367120049999912, 0.0),
- RSPEC_GEO_FACTORY.point(-170.79645748599992, -14.339939059999907, 0.0),
- RSPEC_GEO_FACTORY.point(-170.82282467399992, -14.326755466999956, 0.0),
- RSPEC_GEO_FACTORY.point(-170.83124752499987, -14.319431247999944, 0.0),
- RSPEC_GEO_FACTORY.point(-170.78864498599992, -14.294528903999918, 0.0),
- RSPEC_GEO_FACTORY.point(-170.77257239499986, -14.291436455999929, 0.0),
- RSPEC_GEO_FACTORY.point(-170.7378637359999, -14.292087497999887, 0.0),
- RSPEC_GEO_FACTORY.point(-170.72150631399987, -14.289239190999936, 0.0),
- RSPEC_GEO_FACTORY.point(-170.69847571499992, -14.260511976999894, 0.0),
- RSPEC_GEO_FACTORY.point(-170.66144771999987, -14.252373955999872, 0.0),
- RSPEC_GEO_FACTORY.point(-170.62006588399993, -14.254571221999868, 0.0)]
- )
- ),
-
- RSPEC_GEO_FACTORY.polygon(
- RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-169.44013424399992, -14.245293877999913, 0.0),
- RSPEC_GEO_FACTORY.point(-169.44713294199988, -14.255629164999917, 0.0),
- RSPEC_GEO_FACTORY.point(-169.46015377499987, -14.250420830999914, 0.0),
- RSPEC_GEO_FACTORY.point(-169.46808834499996, -14.258721612999906, 0.0),
- RSPEC_GEO_FACTORY.point(-169.4761856759999, -14.262383721999853, 0.0),
- RSPEC_GEO_FACTORY.point(-169.48497473899994, -14.261976820999848, 0.0),
- RSPEC_GEO_FACTORY.point(-169.49486243399994, -14.257256768999937, 0.0),
- RSPEC_GEO_FACTORY.point(-169.49836178299995, -14.2660458309999, 0.0),
- RSPEC_GEO_FACTORY.point(-169.50426184799989, -14.270603122999944, 0.0),
- RSPEC_GEO_FACTORY.point(-169.51252193899995, -14.271742445999891, 0.0),
- RSPEC_GEO_FACTORY.point(-169.52281653599988, -14.27092864399988, 0.0),
- RSPEC_GEO_FACTORY.point(-169.52550208199995, -14.258965752999941, 0.0),
- RSPEC_GEO_FACTORY.point(-169.52928626199989, -14.248793226999894, 0.0),
- RSPEC_GEO_FACTORY.point(-169.53477942599991, -14.241143487999878, 0.0),
- RSPEC_GEO_FACTORY.point(-169.54267330599987, -14.236748955999886, 0.0),
- RSPEC_GEO_FACTORY.point(-169.5275365879999, -14.22600676899988, 0.0),
- RSPEC_GEO_FACTORY.point(-169.50645911399988, -14.222263278999932, 0.0),
- RSPEC_GEO_FACTORY.point(-169.4638565749999, -14.223239841999913, 0.0),
- RSPEC_GEO_FACTORY.point(-169.44404049399992, -14.230645440999893, 0.0),
- RSPEC_GEO_FACTORY.point(-169.44013424399992, -14.245293877999913, 0.0)]
- )
- ),
-
- RSPEC_GEO_FACTORY.polygon(
- RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-169.6356095039999, -14.17701588299991, 0.0),
- RSPEC_GEO_FACTORY.point(-169.6601456369999, -14.189141533999901, 0.0),
- RSPEC_GEO_FACTORY.point(-169.6697485019999, -14.187920830999886, 0.0),
- RSPEC_GEO_FACTORY.point(-169.67621822799987, -14.174899997999901, 0.0),
- RSPEC_GEO_FACTORY.point(-169.67617753799988, -14.174899997999901, 0.0),
- RSPEC_GEO_FACTORY.point(-169.66816158799995, -14.169122002999927, 0.0),
- RSPEC_GEO_FACTORY.point(-169.65819251199994, -14.168877862999892, 0.0),
- RSPEC_GEO_FACTORY.point(-169.6471654939999, -14.172133070999848, 0.0),
- RSPEC_GEO_FACTORY.point(-169.6356095039999, -14.17701588299991, 0.0)]
- )
- ),
-
- RSPEC_GEO_FACTORY.polygon(
- RSPEC_GEO_FACTORY.line_string(
- [RSPEC_GEO_FACTORY.point(-171.07347571499992, -11.062107028999876, 0.0),
- RSPEC_GEO_FACTORY.point(-171.08153235599985, -11.066094658999859, 0.0),
- RSPEC_GEO_FACTORY.point(-171.08653723899988, -11.060316664999888, 0.0),
- RSPEC_GEO_FACTORY.point(-171.0856420559999, -11.05136484199987, 0.0),
- RSPEC_GEO_FACTORY.point(-171.0728246739999, -11.052504164999903, 0.0),
- RSPEC_GEO_FACTORY.point(-171.07347571499992, -11.062107028999876, 0.0)]
- )
- )
- ]
- ) }
-
let(:point0) { RSPEC_GEO_FACTORY.point(0, 0, 0.0) }
let(:point1) { RSPEC_GEO_FACTORY.point(-29, -16, 0.0) }
let(:point2) { RSPEC_GEO_FACTORY.point(-25, -18, 0.0) }
let(:point3) { RSPEC_GEO_FACTORY.point(-28, -21, 0.0) }
- let(:point4) { RSPEC_GEO_FACTORY.point(-19, -18, 0.0) }
let(:point5) { RSPEC_GEO_FACTORY.point(3, -14, 0.0) }
let(:point6) { RSPEC_GEO_FACTORY.point(6, -12.9, 0.0) }
let(:point7) { RSPEC_GEO_FACTORY.point(5, -16, 0.0) }
@@ -294,9 +46,6 @@
let(:point17) { RSPEC_GEO_FACTORY.point(-19.6, -13, 0.0) }
let(:point18) { RSPEC_GEO_FACTORY.point(-7.6, 14.2, 0.0) }
let(:point19) { RSPEC_GEO_FACTORY.point(-4.6, 11.9, 0.0) }
- let(:point20) { RSPEC_GEO_FACTORY.point(-8, -4, 0.0) }
- let(:point21) { RSPEC_GEO_FACTORY.point(-4, -8, 0.0) }
- let(:point22) { RSPEC_GEO_FACTORY.point(-10, -6, 0.0) }
let(:shape_a1) { RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(-32, 21, 0.0),
RSPEC_GEO_FACTORY.point(-25, 21, 0.0),
@@ -475,107 +224,8 @@
let(:box_3) { RSPEC_GEO_FACTORY.polygon(list_t3) }
let(:box_4) { RSPEC_GEO_FACTORY.polygon(list_t4) }
- let(:all_shapes) { RSPEC_GEO_FACTORY.collection([
- rooms20nn,
- point0,
- point1,
- point2,
- point3,
- point4,
- point5,
- point6,
- point7,
- point8,
- point9,
- point10,
- point11,
- point12,
- point13,
- point14,
- point15,
- point16,
- point17,
- point18,
- point19,
- point20,
- point21,
- point22,
- shape_a1,
- shape_b,
- shape_c,
- shape_d,
- shape_e,
- shape_f,
- shape_g,
- shape_h,
- shape_i,
- shape_j,
- shape_k,
- shape_l,
- box_1,
- box_2,
- box_3,
- box_4]) }
-
- let(:convex_hull) { all_shapes.convex_hull }
-
let(:point_m1_p0) { RSPEC_GEO_FACTORY.point(33, 28) } # upper left corner of M1
- let(:all_wkt_names) { [
- [convex_hull.exterior_ring, 'Outer Limits'],
- [shape_a1, 'A'],
- [shape_b, 'B'],
- [shape_c1, 'C1'],
- [shape_c2, 'C2'],
- [shape_c3, 'C3'],
- [shape_d, 'D'],
- [shape_e2, 'E2'],
- [shape_e1, 'E1'],
- [shape_e3, 'E3'],
- [shape_e4, 'E4'],
- [shape_e5, 'E5'],
- [shape_f1, 'F1'],
- [shape_f2, 'F2'],
- [shape_g1, 'G1'],
- [shape_g2, 'G2'],
- [shape_g3, 'G3'],
- [shape_h, 'H'],
- [shape_i, 'I'],
- [shape_j, 'J'],
- [shape_k, 'K'],
- [shape_l, 'L'],
- [room2020, 'Room 2020'],
- [room2022, 'Room 2022'],
- [room2024, 'Room 2024'],
- [point0, 'P0'],
- [point1, 'P1'],
- [point2, 'P2'],
- [point3, 'P3'],
- [point4, 'P4'],
- [point5, 'P5'],
- [point6, 'P6'],
- [point7, 'P7'],
- [point8, 'P8'],
- [point9, 'P9'],
- [point10, 'P10'],
- [point11, 'P11'],
- [point12, 'P12'],
- [point13, 'P13'],
- [point14, 'P14'],
- [point15, 'P15'],
- [point16, 'P16'],
- [point17, 'P17'],
- [point18, 'P18'],
- [point19, 'P19'],
- [point20, 'P20'],
- [point21, 'P21'],
- [point22, 'P22'],
- [box_1, 'Box_1'],
- [box_2, 'Box_2'],
- [box_3, 'Box_3'],
- [box_4, 'Box_4']
- ].freeze }
-
let(:e1_and_e2) { RSPEC_GEO_FACTORY.parse_wkt('POLYGON ((-9.0 6.0 0.0, -9.0 2.0 0.0, ' \
'-14.0 2.0 0.0, -14.0 6.0 0.0, ' \
'-9.0 6.0 0.0))') }
@@ -617,15 +267,6 @@
RSPEC_GEO_FACTORY.point(0, 0, 0.0)])
}
- # sub_list_box_b is completely inside list_box_b, specifically so that it can be found in list_box_b
- let(:sub_list_box_b) {
- RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(0.5, -0.5, 0.0),
- RSPEC_GEO_FACTORY.point(9.5, -0.5, 0.0),
- RSPEC_GEO_FACTORY.point(9.5, -9.5, 0.0),
- RSPEC_GEO_FACTORY.point(0.5, -9.5, 0.0),
- RSPEC_GEO_FACTORY.point(0.5, -0.5, 0.0)])
- }
-
let(:list_box_c) {
RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(-10, 0, 0.0),
RSPEC_GEO_FACTORY.point(0, 0, 0.0),
@@ -634,14 +275,6 @@
RSPEC_GEO_FACTORY.point(-10, 0, 0.0)])
}
- let(:list_box_d) {
- RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(-10, 10, 0.0),
- RSPEC_GEO_FACTORY.point(0, 10, 0.0),
- RSPEC_GEO_FACTORY.point(0, 0, 0.0),
- RSPEC_GEO_FACTORY.point(-10, 0, 0.0),
- RSPEC_GEO_FACTORY.point(-10, 10, 0.0)])
- }
-
let(:list_box_e) {
RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(0, 10, 0.0),
RSPEC_GEO_FACTORY.point(10, 10, 0.0),
@@ -650,81 +283,28 @@
RSPEC_GEO_FACTORY.point(0, 10, 0.0)])
}
- let(:list_box_f) {
- RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(-10, 10, 0.0),
- RSPEC_GEO_FACTORY.point(10, 10, 0.0),
- RSPEC_GEO_FACTORY.point(10, -10, 0.0),
- RSPEC_GEO_FACTORY.point(-10, -10, 0.0),
- RSPEC_GEO_FACTORY.point(-10, 10, 0.0)])
- }
-
- let(:list_box_l2) {
- RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(-10, 10, 0.0),
- RSPEC_GEO_FACTORY.point(0, 10, 0.0),
- RSPEC_GEO_FACTORY.point(0, -10, 0.0),
- RSPEC_GEO_FACTORY.point(-10, -10, 0.0),
- RSPEC_GEO_FACTORY.point(-10, 10, 0.0)])
- }
-
- let(:list_box_r2) {
- RSPEC_GEO_FACTORY.line_string([RSPEC_GEO_FACTORY.point(0, 10, 0.0),
- RSPEC_GEO_FACTORY.point(10, 10, 0.0),
- RSPEC_GEO_FACTORY.point(10, -10, 0.0),
- RSPEC_GEO_FACTORY.point(0, -10, 0.0),
- RSPEC_GEO_FACTORY.point(0, 10, 0.0)])
- }
-
let(:box_a) { RSPEC_GEO_FACTORY.polygon(list_box_a) }
- let(:sub_box_a) { RSPEC_GEO_FACTORY.polygon(sub_list_box_a) }
let(:box_b) { RSPEC_GEO_FACTORY.polygon(list_box_b) }
- let(:sub_box_b) { RSPEC_GEO_FACTORY.polygon(sub_list_box_b) }
let(:box_c) { RSPEC_GEO_FACTORY.polygon(list_box_c) }
- let(:box_d) { RSPEC_GEO_FACTORY.polygon(list_box_d) }
let(:box_e) { RSPEC_GEO_FACTORY.polygon(list_box_e) }
- let(:box_f) { RSPEC_GEO_FACTORY.polygon(list_box_f) }
- let(:box_l2) { RSPEC_GEO_FACTORY.polygon(list_box_l2) }
- let(:box_r2) { RSPEC_GEO_FACTORY.polygon(list_box_r2) }
let(:new_box_a) { FactoryBot.create(
- :geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_a]),
+ :geographic_item,
+ geography: RSPEC_GEO_FACTORY.multi_polygon([box_a]),
by: geo_user) }
- let(:new_sub_box_a) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([sub_box_a]),
- by: geo_user) }
-
- let(:new_box_b) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_b]),
- by: geo_user) }
-
- let(:new_sub_box_b) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([sub_box_b]),
+ let(:new_box_b) { FactoryBot.create(:geographic_item,
+ geography: RSPEC_GEO_FACTORY.multi_polygon([box_b]),
by: geo_user) }
- let(:new_box_c) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_c]),
+ let(:new_box_c) { FactoryBot.create(:geographic_item,
+ geography: RSPEC_GEO_FACTORY.multi_polygon([box_c]),
by: geo_user) }
- let(:new_box_d) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_d]),
+ let(:new_box_e) { FactoryBot.create(:geographic_item,
+ geography: RSPEC_GEO_FACTORY.multi_polygon([box_e]),
by: geo_user) }
- let(:new_box_e) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_e]),
- by: geo_user) }
-
- let(:new_box_l) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_l]),
- by: geo_user) }
-
- let(:new_box_f) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_f]),
- by: geo_user) }
-
- let(:new_box_l2) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: RSPEC_GEO_FACTORY.multi_polygon([box_l2]),
- by: geo_user) }
=begin
Small World is a 2 by 2 matrix of squares, centered aroung co-ordinates 0, 0, and
@@ -775,18 +355,6 @@
geographic_areas_geographic_items_attributes: [{geographic_item: new_box_a }]) # NOTE: data_origin nil
}
- let(:sub_area_a) {
- FactoryBot.create(
- :level2_geographic_area,
- name: 'sub_box a',
- geographic_area_type: parish_gat,
- iso_3166_a3: nil,
- iso_3166_a2: nil,
- parent: area_a,
- by: geo_user,
- geographic_areas_geographic_items_attributes: [{geographic_item: new_sub_box_a}])
- }
-
let(:area_b) {
FactoryBot.create(
:level1_geographic_area,
@@ -799,42 +367,6 @@
geographic_areas_geographic_items_attributes: [{geographic_item: new_box_b}])
}
- let(:sub_area_b) {
- FactoryBot.create(
- :level2_geographic_area,
- name: 'sub_box b',
- geographic_area_type: parish_gat,
- iso_3166_a3: nil,
- iso_3166_a2: nil,
- parent: area_b,
- by: geo_user,
- geographic_areas_geographic_items_attributes: [{geographic_item: new_sub_box_b}])
- }
-
- let(:area_c) {
- FactoryBot.create(
- :level1_geographic_area,
- name: 'C',
- geographic_area_type: country_gat,
- iso_3166_a3: nil,
- iso_3166_a2: nil,
- parent: area_f,
- by: geo_user,
- geographic_areas_geographic_items_attributes: [{geographic_item: new_box_c}])
- }
-
- let(:area_d) {
- FactoryBot.create(
- :level1_geographic_area,
- name: 'D',
- geographic_area_type: country_gat,
- iso_3166_a3: nil,
- iso_3166_a2: nil,
- parent: area_f,
- by: geo_user,
- geographic_areas_geographic_items_attributes: [{geographic_item: new_box_d}])
- }
-
let(:area_e) {
FactoryBot.create(
:level0_geographic_area,
@@ -859,36 +391,6 @@
geographic_areas_geographic_items_attributes: [{geographic_item: new_box_e}])
}
- let(:area_l2) {
- FactoryBot.create(
- :level0_geographic_area,
- name: 'L2',
- geographic_area_type: country_gat,
- iso_3166_a3: nil,
- iso_3166_a2: nil,
- parent: earth,
- by: geo_user,
- geographic_areas_geographic_items_attributes: [{geographic_item: new_box_e}])
- }
-
- # AssertedDistributions
- let(:source2) { FactoryBot.create(:valid_source, by: geo_user) }
- let(:cite2) do
- FactoryBot.create(:valid_citation, {citation_object: by_bill,
- source: source2,
- by: geo_user,
- project: geo_project})
- end
- let(:ad2) do
- ad = AssertedDistribution.new(otu: by_bill,
- geographic_area: sub_area_b,
- by: geo_user,
- project: geo_project)
- ad.origin_citation = cite2
- ad.save!
- ad
- end
-
# Collecting Events
let(:ce_p0) { FactoryBot.create(:collecting_event, verbatim_label: '@ce_p0') }
@@ -1012,57 +514,11 @@
api_request: 'gr_p4s',
collecting_event: ce_p4s,
error_geographic_item: nil,
- geographic_item: GeographicItem.new(point: new_box_c.st_centroid, by: geo_user)) }
-
- let(:otu_a) {
- Otu.create!(
- name: 'Otu_A',
- taxon_name: geo_species,
- by: geo_user,
- project: geo_project
- )
- }
+ geographic_item: GeographicItem.new(geography: new_box_c.st_centroid, by: geo_user)) }
# Collection objects
- let(:co_a) {
- co = FactoryBot.create(
- :valid_collection_object,
- created_at: '2000/01/01',
- updated_at: '2000/07/01',
- collecting_event: ce_a,
- project: geo_project,
- by: geo_user)
-
- TaxonDetermination.create!(taxon_determination_object: co, otu: otu_a, by: geo_user, project: geo_project)
-
- TaxonDetermination.create!(taxon_determination_object: co, otu: top_dog, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: by_bill, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: abra, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: cadabra, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: alakazam, by: geo_user, project: geo_project)
-
- co
- }
-
- let(:co_b) {
- co = FactoryBot.create(
- :valid_collection_object,
- created_at: '2001/01/01',
- updated_at: '2001/07/01',
- collecting_event: ce_b,
- project: geo_project,
- by: geo_user
- )
-
- TaxonDetermination.create!(taxon_determination_object: co, otu: otu_p4, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: nuther_dog, by: geo_user, project: geo_project)
- TaxonDetermination.create!(taxon_determination_object: co, otu: spooler, by: geo_user, project: geo_project)
-
- co
- }
-
- let(:p_a) { GeographicItem::Point.create!(point: new_box_a.st_centroid, by: geo_user) }
- let(:p_b) { GeographicItem::Point.create!(point: new_box_b.st_centroid, by: geo_user) }
+ let(:p_a) { GeographicItem.create!(geography: new_box_a.st_centroid, by: geo_user) }
+ let(:p_b) { GeographicItem.create!(geography: new_box_b.st_centroid, by: geo_user) }
let(:gr_a) { Georeference::VerbatimData.create!(
api_request: 'area_a',
@@ -1082,7 +538,7 @@
RSPEC_GEO_FACTORY.point(2.5, -2.5, 0.0)])
}
- let(:err_b) { GeographicItem::Polygon.create!(polygon: RSPEC_GEO_FACTORY.polygon(polygon_inner), by: geo_user) }
+ let(:err_b) { GeographicItem.create!(geography: RSPEC_GEO_FACTORY.polygon(polygon_inner), by: geo_user) }
let(:gr_b) {
Georeference::VerbatimData.create!(
@@ -1101,111 +557,72 @@
let(:ted) { Person.create!(last_name: 'Pomaroy', first_name: 'Ted', prefix: 'HEWIC', by: geo_user) }
let(:bill) { Person.create!(first_name: 'Bill', last_name: 'Ardson', by: geo_user) }
- # need some otus
- let(:top_dog) { Otu.create!(
- name: 'Top Dog',
- taxon_name: geo_family1,
- project: geo_project,
- by: geo_user
- )
- }
-
- let(:by_bill) {
- Otu.create!(
- name: 'Top Dog (by Bill)',
- taxon_name: geo_family1,
- by: geo_user,
- project: geo_project
- )
- }
-
- let(:nuther_dog) {
- Otu.create!(
- name: 'Another Dog',
- taxon_name: geo_family2,
- by: geo_user,
- project: geo_project,
- )
- }
-
#
# Possible logical break (above/below)
#
# GeographicItem interactions
- let(:all_items) { FactoryBot.create(:geographic_item_geometry_collection,
- geometry_collection: all_shapes.as_binary) } # 54
- let(:outer_limits) { FactoryBot.create(:geographic_item_line_string,
- line_string: convex_hull.exterior_ring.as_binary) } # 55
-
- let(:p0) { FactoryBot.create(:geographic_item_point, point: point0.as_binary, by: geo_user) } # 0
- let(:p1) { FactoryBot.create(:geographic_item_point, point: point1.as_binary, by: geo_user) }
- let(:p2) { FactoryBot.create(:geographic_item_point, point: point2.as_binary, by: geo_user) } # 2
- let(:p3) { FactoryBot.create(:geographic_item_point, point: point3.as_binary, by: geo_user) } # 3
- let(:p4) { FactoryBot.create(:geographic_item_point, point: point4.as_binary, by: geo_user) } # 3
- let(:p5) { FactoryBot.create(:geographic_item_point, point: point5.as_binary, by: geo_user) } # 5
- let(:p6) { FactoryBot.create(:geographic_item_point, point: point6.as_binary, by: geo_user) } # 6
- let(:p7) { FactoryBot.create(:geographic_item_point, point: point7.as_binary, by: geo_user) } # 7
- let(:p8) { FactoryBot.create(:geographic_item_point, point: point8.as_binary, by: geo_user) } # 8
- let(:p9) { FactoryBot.create(:geographic_item_point, point: point9.as_binary, by: geo_user) } # 9
- let(:p10) { FactoryBot.create(:geographic_item_point, point: point10.as_binary, by: geo_user) } # 10
- let(:p11) { FactoryBot.create(:geographic_item_point, point: point11.as_binary, by: geo_user) } # 11
- let(:p12) { FactoryBot.create(:geographic_item_point, point: point12.as_binary, by: geo_user) } # 10
- let(:p13) { FactoryBot.create(:geographic_item_point, point: point13.as_binary, by: geo_user) } # 10
- let(:p14) { FactoryBot.create(:geographic_item_point, point: point14.as_binary, by: geo_user) } # 14
- let(:p15) { FactoryBot.create(:geographic_item_point, point: point15.as_binary, by: geo_user) } # 15
- let(:p16) { FactoryBot.create(:geographic_item_point, point: point16.as_binary, by: geo_user) } # 16
- let(:p17) { FactoryBot.create(:geographic_item_point, point: point17.as_binary, by: geo_user) } # 17
- let(:p18) { FactoryBot.create(:geographic_item_point, point: point18.as_binary, by: geo_user) } # 18
- let(:p19) { FactoryBot.create(:geographic_item_point, point: point19.as_binary, by: geo_user) } # 19
-
- let(:a) { FactoryBot.create(:geographic_item_line_string, line_string: shape_a1.as_binary, by: geo_user) } # 24
- let(:b) { FactoryBot.create(:geographic_item_polygon, polygon: shape_b.as_binary, by: geo_user) } # 27
- let(:c1) { FactoryBot.create(:geographic_item_line_string, line_string: shape_c1, by: geo_user) } # 28
- let(:c2) { FactoryBot.create(:geographic_item_line_string, line_string: shape_c2, by: geo_user) } # 28
- let(:c3) { FactoryBot.create(:geographic_item_line_string, line_string: shape_c3, by: geo_user) } # 29
- let(:c) { FactoryBot.create(:geographic_item_multi_line_string,
- multi_line_string: shape_c.as_binary, by: geo_user) } # 30
-
- let(:d) { FactoryBot.create(:geographic_item_line_string, line_string: shape_d.as_binary, by: geo_user) }
-
- let(:b1) { FactoryBot.create(:geographic_item_polygon, polygon: shape_b_outer.as_binary, by: geo_user) } # 25
- let(:b2) { FactoryBot.create(:geographic_item_polygon, polygon: shape_b_inner.as_binary, by: geo_user) } # 26
+ let(:p0) { FactoryBot.create(:geographic_item, geography: point0.as_binary, by: geo_user) } # 0
+ let(:p1) { FactoryBot.create(:geographic_item, geography: point1.as_binary, by: geo_user) }
+ let(:p2) { FactoryBot.create(:geographic_item, geography: point2.as_binary, by: geo_user) } # 2
+ let(:p3) { FactoryBot.create(:geographic_item, geography: point3.as_binary, by: geo_user) } # 3
+ let(:p5) { FactoryBot.create(:geographic_item, geography: point5.as_binary, by: geo_user) } # 5
+ let(:p6) { FactoryBot.create(:geographic_item, geography: point6.as_binary, by: geo_user) } # 6
+ let(:p7) { FactoryBot.create(:geographic_item, geography: point7.as_binary, by: geo_user) } # 7
+ let(:p8) { FactoryBot.create(:geographic_item, geography: point8.as_binary, by: geo_user) } # 8
+ let(:p9) { FactoryBot.create(:geographic_item, geography: point9.as_binary, by: geo_user) } # 9
+ let(:p10) { FactoryBot.create(:geographic_item, geography: point10.as_binary, by: geo_user) } # 10
+ let(:p11) { FactoryBot.create(:geographic_item, geography: point11.as_binary, by: geo_user) } # 11
+ let(:p12) { FactoryBot.create(:geographic_item, geography: point12.as_binary, by: geo_user) } # 10
+ let(:p13) { FactoryBot.create(:geographic_item, geography: point13.as_binary, by: geo_user) } # 10
+ let(:p14) { FactoryBot.create(:geographic_item, geography: point14.as_binary, by: geo_user) } # 14
+ let(:p15) { FactoryBot.create(:geographic_item, geography: point15.as_binary, by: geo_user) } # 15
+ let(:p16) { FactoryBot.create(:geographic_item, geography: point16.as_binary, by: geo_user) } # 16
+ let(:p17) { FactoryBot.create(:geographic_item, geography: point17.as_binary, by: geo_user) } # 17
+ let(:p18) { FactoryBot.create(:geographic_item, geography: point18.as_binary, by: geo_user) } # 18
+ let(:p19) { FactoryBot.create(:geographic_item, geography: point19.as_binary, by: geo_user) } # 19
+
+ let(:a) { FactoryBot.create(:geographic_item, geography: shape_a1.as_binary, by: geo_user) } # 24
+ let(:b) { FactoryBot.create(:geographic_item, geography: shape_b.as_binary, by: geo_user) } # 27
+ let(:c1) { FactoryBot.create(:geographic_item, geography: shape_c1, by: geo_user) } # 28
+ let(:c2) { FactoryBot.create(:geographic_item, geography: shape_c2, by: geo_user) } # 28
+ let(:c3) { FactoryBot.create(:geographic_item, geography: shape_c3, by: geo_user) } # 29
+ let(:c) { FactoryBot.create(:geographic_item,
+ geography: shape_c.as_binary, by: geo_user) } # 30
+
+ let(:d) { FactoryBot.create(:geographic_item, geography: shape_d.as_binary, by: geo_user) }
+
+ let(:b1) { FactoryBot.create(:geographic_item, geography: shape_b_outer.as_binary, by: geo_user) } # 25
+ let(:b2) { FactoryBot.create(:geographic_item, geography: shape_b_inner.as_binary, by: geo_user) } # 26
let(:e0) { e.geo_object } # a collection of polygons
- let(:e1) { FactoryBot.create(:geographic_item_polygon, polygon: poly_e1.as_binary, by: geo_user) } # 35
- let(:e2) { FactoryBot.create(:geographic_item_polygon, polygon: poly_e2.as_binary, by: geo_user) } # 33
- let(:e3) { FactoryBot.create(:geographic_item_polygon, polygon: poly_e3.as_binary, by: geo_user) } # 34
- let(:e4) { FactoryBot.create(:geographic_item_polygon, polygon: poly_e4.as_binary, by: geo_user) } # 35
- let(:e5) { FactoryBot.create(:geographic_item_polygon, polygon: poly_e5.as_binary, by: geo_user) } # 35
- let(:e) { FactoryBot.create(:geographic_item_geometry_collection,
- geometry_collection: shape_e.as_binary, by: geo_user) } # 37
- let(:f1) { FactoryBot.create(:geographic_item_line_string, line_string: shape_f1.as_binary, by: geo_user) } # 38
- let(:f2) { FactoryBot.create(:geographic_item_line_string, line_string: shape_f2.as_binary, by: geo_user) } # 39
-
- let(:f) { FactoryBot.create(:geographic_item_multi_line_string,
- multi_line_string: shape_f.as_binary, by: geo_user) } # 40
- let(:g1) { FactoryBot.create(:geographic_item_polygon, polygon: shape_g1.as_binary, by: geo_user) } # 41
- let(:g2) { FactoryBot.create(:geographic_item_polygon, polygon: shape_g2.as_binary, by: geo_user) } # 42
- let(:g3) { FactoryBot.create(:geographic_item_polygon, polygon: shape_g3.as_binary, by: geo_user) } # 43
- let(:g) { FactoryBot.create(:geographic_item_multi_polygon,
- multi_polygon: shape_g.as_binary, by: geo_user) } # 44
- let(:h) { FactoryBot.create(:geographic_item_multi_point, multi_point: shape_h.as_binary, by: geo_user) } # 45
- let(:j) { FactoryBot.create(:geographic_item_geometry_collection, geometry_collection: shape_j, by: geo_user) } # 47
- let(:k) { FactoryBot.create(:geographic_item_polygon, polygon: shape_k.as_binary, by: geo_user) }
- let(:l) { FactoryBot.create(:geographic_item_line_string, line_string: shape_l.as_binary, by: geo_user) } # 49
-
- let(:shapeE1) { e0.geometry_n(0) }
- let(:shapeE2) { e0.geometry_n(1) }
- let(:shapeE3) { e0.geometry_n(2) }
- let(:shapeE4) { e0.geometry_n(3) }
- let(:shapeE5) { e0.geometry_n(4) }
+ let(:e1) { FactoryBot.create(:geographic_item, geography: poly_e1.as_binary, by: geo_user) } # 35
+ let(:e2) { FactoryBot.create(:geographic_item, geography: poly_e2.as_binary, by: geo_user) } # 33
+ let(:e3) { FactoryBot.create(:geographic_item, geography: poly_e3.as_binary, by: geo_user) } # 34
+ let(:e4) { FactoryBot.create(:geographic_item, geography: poly_e4.as_binary, by: geo_user) } # 35
+ let(:e5) { FactoryBot.create(:geographic_item, geography: poly_e5.as_binary, by: geo_user) } # 35
+ let(:e) { FactoryBot.create(:geographic_item,
+ geography: shape_e.as_binary, by: geo_user) } # 37
+ let(:f1) { FactoryBot.create(:geographic_item, geography: shape_f1.as_binary, by: geo_user) } # 38
+ let(:f2) { FactoryBot.create(:geographic_item, geography: shape_f2.as_binary, by: geo_user) } # 39
+
+ let(:f) { FactoryBot.create(:geographic_item,
+ geography: shape_f.as_binary, by: geo_user) } # 40
+ let(:g1) { FactoryBot.create(:geographic_item, geography: shape_g1.as_binary, by: geo_user) } # 41
+ let(:g2) { FactoryBot.create(:geographic_item, geography: shape_g2.as_binary, by: geo_user) } # 42
+ let(:g3) { FactoryBot.create(:geographic_item, geography: shape_g3.as_binary, by: geo_user) } # 43
+ let(:g) { FactoryBot.create(:geographic_item,
+ geography: shape_g.as_binary, by: geo_user) } # 44
+ let(:h) { FactoryBot.create(:geographic_item, geography: shape_h.as_binary, by: geo_user) } # 45
+ let(:j) { FactoryBot.create(:geographic_item, geography: shape_j, by: geo_user) } # 47
+ let(:k) { FactoryBot.create(:geographic_item, geography: shape_k.as_binary, by: geo_user) }
+ let(:l) { FactoryBot.create(:geographic_item, geography: shape_l.as_binary, by: geo_user) } # 49
let(:r) { a.geo_object.intersection(p16.geo_object) }
- let(:item_a) { FactoryBot.create(:geographic_item_polygon, polygon: box_1, by: geo_user) } # 57
- let(:item_b) { FactoryBot.create(:geographic_item_polygon, polygon: box_2, by: geo_user) } # 58
- let(:item_c) { FactoryBot.create(:geographic_item_polygon, polygon: box_3, by: geo_user) } # 59
- let(:item_d) { FactoryBot.create(:geographic_item_polygon, polygon: box_4, by: geo_user) } # 60
+ let(:item_a) { FactoryBot.create(:geographic_item, geography: box_1, by: geo_user) } # 57
+ let(:item_b) { FactoryBot.create(:geographic_item, geography: box_2, by: geo_user) } # 58
+ let(:item_c) { FactoryBot.create(:geographic_item, geography: box_3, by: geo_user) } # 59
+ let(:item_d) { FactoryBot.create(:geographic_item, geography: box_4, by: geo_user) } # 60
let(:ce_p1s) { FactoryBot.create(:collecting_event, verbatim_label: '@ce_p1s collect_event test', by: geo_user, project: geo_project) }
@@ -1337,13 +754,13 @@
let(:champaign) {
cc = parent_county
- cc.geographic_items << GeographicItem::MultiPolygon.create!(multi_polygon: cc_shape, by: geo_user)
+ cc.geographic_items << GeographicItem.create!(geography: cc_shape, by: geo_user)
cc
}
let(:illinois) {
il = champaign.parent
- il.geographic_items << GeographicItem::MultiPolygon.create!(multi_polygon: il_shape, by: geo_user)
+ il.geographic_items << GeographicItem.create!(geography: il_shape, by: geo_user)
il
}
@@ -1356,11 +773,6 @@
usa.parent
}
- let(:r2020) { FactoryBot.create(:geographic_item_point, point: room2020.as_binary, by: geo_user) } # 50
- let(:r2022) { FactoryBot.create(:geographic_item_point, point: room2022.as_binary, by: geo_user) } # 51
- let(:r2024) { FactoryBot.create(:geographic_item_point, point: room2024.as_binary, by: geo_user) } # 52
- let(:rooms) { FactoryBot.create(:geographic_item_multi_point, multi_point: rooms20nn.as_binary, by: geo_user) } # 53
-
=begin
Objects in Big Boxia.
@@ -1726,15 +1138,15 @@
api_request: 'gr_n3_ob',
collecting_event: ce_old_boxia_2,
error_geographic_item: item_ob,
- geographic_item: GeographicItem.new(point: item_n3.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_n3.st_centroid)) }
let(:ce_old_boxia_2) { FactoryBot.create(:collecting_event,
start_date_year: 1993,
start_date_month: 3,
start_date_day: 3,
verbatim_label: '@ce_old_boxia_2',
geographic_area: area_old_boxia) }
- let(:item_m4) { FactoryBot.create(:geographic_item, multi_polygon: shape_m4) }
- let(:item_r) { FactoryBot.create(:geographic_item, multi_polygon: shape_r) }
+ let(:item_m4) { FactoryBot.create(:geographic_item, geography: shape_m4) }
+ let(:item_r) { FactoryBot.create(:geographic_item, geography: shape_r) }
let(:shape_r) { RspecGeoHelpers.make_multipolygon(shape_m3[0]
.exterior_ring.points[0], 0, 0, 2, 2) }
let(:shape_m4) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 0, 3, 1, 1) }
@@ -1781,7 +1193,7 @@
api_request: 'gr_n3',
collecting_event: ce_n3,
error_geographic_item: item_n3,
- geographic_item: GeographicItem.new(point: item_n3.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_n3.st_centroid)) }
let(:co_n3) { FactoryBot.create(:valid_collection_object, { collecting_event: ce_n3 }) }
let(:area_n3) {
area = FactoryBot.create(:level1_geographic_area,
@@ -1794,7 +1206,7 @@
area
}
let(:shape_m3) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 0, 2, 1, 1) }
- let(:item_n3) { FactoryBot.create(:geographic_item, multi_polygon: shape_n3) }
+ let(:item_n3) { FactoryBot.create(:geographic_item, geography: shape_n3) }
let(:shape_n3) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 1, 2, 1, 1) }
# build the collecting event for an object in P1(B), part of Big Boxia
@@ -1809,7 +1221,7 @@
api_request: 'gr_p1b',
collecting_event: ce_p1b,
error_geographic_item: item_p1b,
- geographic_item: GeographicItem.new(point: item_p1b.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_p1b.st_centroid)) }
let(:area_land_mass) {
area = FactoryBot.create(:level0_geographic_area,
name: 'Great Northern Land Mass',
@@ -1845,19 +1257,19 @@
# secondary country shapes
# same shape as Q, different object
- let(:item_bb) { FactoryBot.create(:geographic_item, multi_polygon: shape_q) }
+ let(:item_bb) { FactoryBot.create(:geographic_item, geography: shape_q) }
# superseded country shapes
- let(:item_q) { FactoryBot.create(:geographic_item, multi_polygon: shape_q) }
- let(:item_u) { FactoryBot.create(:geographic_item, multi_polygon: shape_u) }
- let(:item_ob) { FactoryBot.create(:geographic_item, multi_polygon: shape_ob) }
- let(:item_eb_1) { FactoryBot.create(:geographic_item, multi_polygon: shape_eb_1) }
- let(:item_eb_2) { FactoryBot.create(:geographic_item, multi_polygon: shape_eb_2) }
- let(:item_wb) { FactoryBot.create(:geographic_item, multi_polygon: shape_wb) }
- let(:item_p1b) { FactoryBot.create(:geographic_item, multi_polygon: shape_p1b) }
+ let(:item_q) { FactoryBot.create(:geographic_item, geography: shape_q) }
+ let(:item_u) { FactoryBot.create(:geographic_item, geography: shape_u) }
+ let(:item_ob) { FactoryBot.create(:geographic_item, geography: shape_ob) }
+ let(:item_eb_1) { FactoryBot.create(:geographic_item, geography: shape_eb_1) }
+ let(:item_eb_2) { FactoryBot.create(:geographic_item, geography: shape_eb_2) }
+ let(:item_wb) { FactoryBot.create(:geographic_item, geography: shape_wb) }
+ let(:item_p1b) { FactoryBot.create(:geographic_item, geography: shape_p1b) }
# the entire land mass
- let(:item_w) { FactoryBot.create(:geographic_item, multi_polygon: shape_w) }
+ let(:item_w) { FactoryBot.create(:geographic_item, geography: shape_w) }
# some other areas
let(:east_boxia) { [area_east_boxia_1, area_east_boxia_2, area_east_boxia_2] }
@@ -1908,7 +1320,7 @@
api_request: 'gr_o1',
collecting_event: ce_o1,
error_geographic_item: item_o1,
- geographic_item: GeographicItem.new(point: item_o1.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_o1.st_centroid)) }
let(:area_o1) {
area = FactoryBot.create(:level2_geographic_area,
@@ -1920,7 +1332,7 @@
area.save!
area
}
- let(:item_o1) { FactoryBot.create(:geographic_item, multi_polygon: shape_o1) }
+ let(:item_o1) { FactoryBot.create(:geographic_item, geography: shape_o1) }
# build the collecting event for an object in O3
# @ce_o3 has no georeference
@@ -1952,11 +1364,11 @@
area.save!
area
}
- let(:item_s) { FactoryBot.create(:geographic_item, multi_polygon: shape_s) }
+ let(:item_s) { FactoryBot.create(:geographic_item, geography: shape_s) }
let(:shape_s) { RspecGeoHelpers.make_multipolygon(shape_o3[0]
.exterior_ring.points[0], 0, 0, 2, 2) }
let(:shape_o3) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 2, 2, 1, 1) }
- let(:item_o3) { FactoryBot.create(:geographic_item, multi_polygon: shape_o3) }
+ let(:item_o3) { FactoryBot.create(:geographic_item, geography: shape_o3) }
let(:area_so3) {
area = FactoryBot.create(:level1_geographic_area,
name: 'SO3',
@@ -1982,12 +1394,12 @@
api_request: 'gr_n2_a',
collecting_event: ce_n2,
error_geographic_item: item_n2,
- geographic_item: GeographicItem.new(point: item_n2.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_n2.st_centroid)) }
let(:gr_n2_b) { FactoryBot.create(:georeference_verbatim_data,
api_request: 'gr_n2_b',
collecting_event: ce_n2,
error_geographic_item: item_n2,
- geographic_item: GeographicItem.new(point: item_n2.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_n2.st_centroid)) }
let(:area_n2) {
area = FactoryBot.create(:level2_geographic_area,
name: 'N2',
@@ -2008,10 +1420,10 @@
area.save!
area
}
- let(:item_t_1) { FactoryBot.create(:geographic_item, multi_polygon: shape_t_1) }
+ let(:item_t_1) { FactoryBot.create(:geographic_item, geography: shape_t_1) }
let(:shape_t_1) { RspecGeoHelpers.make_multipolygon(shape_m1[0]
.exterior_ring.points[0], 0, 0, 2, 2) }
- let(:item_n2) { FactoryBot.create(:geographic_item, multi_polygon: shape_n2) }
+ let(:item_n2) { FactoryBot.create(:geographic_item, geography: shape_n2) }
let(:shape_n2) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 1, 1, 1, 1) }
let(:area_t_2) {
area = FactoryBot.create(:level1_geographic_area,
@@ -2023,7 +1435,7 @@
area.save!
area
}
- let(:item_t_2) { FactoryBot.create(:geographic_item, multi_polygon: shape_t_2) }
+ let(:item_t_2) { FactoryBot.create(:geographic_item, geography: shape_t_2) }
let(:shape_t_2) { RspecGeoHelpers.make_multipolygon(shape_m1[0]
.exterior_ring.points[0], 0, 0, 2, 2) }
@@ -2038,7 +1450,7 @@
api_request: 'gr_n4',
collecting_event: ce_n4,
error_geographic_item: item_n4,
- geographic_item: GeographicItem.new(point: item_n4.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_n4.st_centroid)) }
let(:co_n4) { FactoryBot.create(:valid_collection_object, { collecting_event: ce_n4 }) }
let(:area_n4) {
@@ -2062,7 +1474,7 @@
area.save!
area
}
- let(:item_n4) { FactoryBot.create(:geographic_item, multi_polygon: shape_n4) }
+ let(:item_n4) { FactoryBot.create(:geographic_item, geography: shape_n4) }
let(:shape_n4) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 1, 3, 1, 1) }
@@ -2089,7 +1501,7 @@
api_request: 'gr_m1',
collecting_event: ce_m1,
error_geographic_item: item_m1,
- geographic_item: GeographicItem.new(point: item_m1.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_m1.st_centroid)) }
let(:ce_m1a) { FactoryBot.create(:collecting_event,
start_date_year: 1971,
@@ -2103,7 +1515,7 @@
api_request: 'gr_m1a',
collecting_event: ce_m1a,
error_geographic_item: item_m1,
- geographic_item: GeographicItem.new(point: item_m1.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_m1.st_centroid)) }
let(:area_m1) {
area = FactoryBot.create(:level2_geographic_area,
@@ -2115,7 +1527,7 @@
area.save!
area
}
- let(:item_m1) { FactoryBot.create(:geographic_item, multi_polygon: shape_m1) }
+ let(:item_m1) { FactoryBot.create(:geographic_item, geography: shape_m1) }
let(:area_qtm1) {
area = FactoryBot.create(:level2_geographic_area,
name: 'QTM1',
@@ -2149,7 +1561,7 @@
# api_request: 'gr_p2',
# collecting_event: @ce_p2,
# error_geographic_item: @item_p2,
- # geographic_item: GeographicItem.new(point: @item_p2.st_centroid))
+ # geographic_item: GeographicItem.new(geography: @item_p2.st_centroid))
let(:co_p2b) { FactoryBot.create(:valid_collection_object, { collecting_event: ce_p2b }) }
let(:area_p2b) {
@@ -2162,7 +1574,7 @@
area.save!
area
}
- let(:item_p2b) { FactoryBot.create(:geographic_item, multi_polygon: shape_p2b) }
+ let(:item_p2b) { FactoryBot.create(:geographic_item, geography: shape_p2b) }
let(:shape_p2b) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 3, 1, 1, 1) }
let(:area_qup2) {
@@ -2199,7 +1611,7 @@
api_request: 'gr_m2 in Big Boxia',
collecting_event: ce_m2,
error_geographic_item: item_m2,
- geographic_item: GeographicItem.new(point: item_m2.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_m2.st_centroid)) }
let(:area_m2) {
area = FactoryBot.create(:level2_geographic_area,
@@ -2211,7 +1623,7 @@
area.save!
area
}
- let(:item_m2) { FactoryBot.create(:geographic_item, multi_polygon: shape_m2) }
+ let(:item_m2) { FactoryBot.create(:geographic_item, geography: shape_m2) }
let(:shape_m2) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 0, 1, 1, 1) }
# build the collecting event for objects in N1
@@ -2226,7 +1638,7 @@
# api_request: 'gr_n1',
# collecting_event: @ce_n1,
# error_geographic_item: @item_n1,
- # geographic_item: GeographicItem.new(point: @item_n1.st_centroid))
+ # geographic_item: GeographicItem.new(geography: @item_n1.st_centroid))
let(:area_n1) {
area = FactoryBot.create(:level2_geographic_area,
@@ -2238,7 +1650,7 @@
area.save!
area
}
- let(:item_n1) { FactoryBot.create(:geographic_item, multi_polygon: shape_n1) }
+ let(:item_n1) { FactoryBot.create(:geographic_item, geography: shape_n1) }
let(:shape_n1) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 1, 0, 1, 1) }
let(:area_qtn1) {
@@ -2263,7 +1675,7 @@
api_request: 'gr_o2',
collecting_event: ce_o2,
error_geographic_item: item_o2,
- geographic_item: GeographicItem.new(point: item_o2.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_o2.st_centroid)) }
let(:co_o2) { FactoryBot.create(:valid_collection_object, { collecting_event: ce_o2 }) }
@@ -2278,7 +1690,7 @@
area
}
- let(:item_o2) { FactoryBot.create(:geographic_item, multi_polygon: shape_o2, by: geo_user) }
+ let(:item_o2) { FactoryBot.create(:geographic_item, geography: shape_o2, by: geo_user) }
let(:shape_o2) { RspecGeoHelpers.make_multipolygon(point_m1_p0, 2, 1, 1, 1) }
# build for collecting event for objects in M3
@@ -2292,7 +1704,7 @@
api_request: 'gr_m3',
collecting_event: ce_m3,
error_geographic_item: item_m3,
- geographic_item: GeographicItem.new(point: item_m3.st_centroid)) }
+ geographic_item: GeographicItem.new(geography: item_m3.st_centroid)) }
let(:co_m3) { FactoryBot.create(:valid_collection_object, { collecting_event: ce_m3 }) }
let(:area_m3) {
@@ -2305,6 +1717,6 @@
area.save!
area
}
- let(:item_m3) { FactoryBot.create(:geographic_item, multi_polygon: shape_m3, by: geo_user) }
+ let(:item_m3) { FactoryBot.create(:geographic_item, geography: shape_m3, by: geo_user) }
end