Browse Source

Merge branch 'feature/addresses' into develop

* feature/addresses:
  Immensely speed up the address lookups by using spatial comparison
  REfactor Place aggregate to use the new address table for geocoding
  Add Address model to fill an address when it cannot be found in the geojson
  fixup! Fall back to "null island" when no centerpoint is given in index.
  Remove unused sql seed file
  Add tooling to import address databases
  Don't git-track any .env file
  Fall back to "null island" when no centerpoint is given in index.
feature/region-slug-gone
Bèr Kessels 5 months ago
parent
commit
6534b205e4

+ 0
- 7
.env.test View File

@@ -1,7 +0,0 @@
PORT=3000
# Postgres Config. See https://www.postgresql.org/docs/9.1/libpq-envars.html
DB_USER=postgres
DB_HOST=localhost
DB_PASSWORD=TinLopeTuckBuckTonsVangBeakKink
DB_NAME=hours_test
DB_PORT=5432

+ 1
- 0
.gitignore View File

@@ -11,6 +11,7 @@

# Used by dotenv library to load environment variables.
.env
.env.*

## Specific to RubyMotion:
.dat*

+ 1
- 0
Makefile View File

@@ -40,6 +40,7 @@ _db-setup:
$(CMD_PREFIX) rake db:event_store
$(CMD_PREFIX) rake db:projections
$(CMD_PREFIX) rake db:seed # TODO: don't run seeds on test
@if [ $(APP_ENV) = 'test' ]; then ./bin/seedaddr < test/fixtures/address_sample.csv; fi

_wait:
sleep 5

+ 17
- 16
app/aggregates/place.rb View File

@@ -1,6 +1,7 @@
# frozen_string_literal: true

require_relative '../events/place_added.rb'
require_relative './place/address.rb'
require_relative './place/place_id.rb'
require_relative './place/region.rb'

@@ -22,8 +23,7 @@ module Hours
PlaceId.new(payload).id)
end

attr_writer :place_id_builder, :region_builder
attr_reader :place_id, :region_slug
attr_writer :address_builder, :place_id_builder, :region_builder

# These apply methods are the hook that this aggregate uses to update
# its internal state from events.
@@ -35,10 +35,9 @@ module Hours
raise DuplicateError, "Place #{id.inspect} already exists" if added?

@payload = Hashie.symbolize_keys(payload)
@place_id = place_id_builder.id
@region_slug = region_builder.region_slug
fill_payload

apply_event(PlaceAdded, aggregate_id: id, body: event_body)
apply_event(PlaceAdded, aggregate_id: id, body: @payload)
self
end

@@ -48,18 +47,20 @@ module Hours
@aggregate_id
end

def event_body
@payload.merge(
properties: properties.merge(
place_id: place_id,
region_slug: region_slug,
'addr:city': city
)
def fill_payload
merge_properties(place_id: place_id_builder.id)
merge_properties(
'addr:city': address_builder.city,
'addr:postcode': address_builder.postcode,
'addr:housenumber': address_builder.number,
'addr:street': address_builder.street
)
merge_properties(region_slug: region_builder.region_slug)
end

def properties
@payload.fetch(:properties, {})
def merge_properties(append_properties)
@payload[:properties] ||= {}
@payload[:properties].merge!(append_properties)
end

def place_id_builder
@@ -70,8 +71,8 @@ module Hours
@region_builder || Region.new(@payload)
end

def city
properties[:'addr:city'] || region_builder.city
def address_builder
@address_builder || Address.new(@payload)
end
end
end

+ 62
- 0
app/aggregates/place/address.rb View File

@@ -0,0 +1,62 @@
# frozen_string_literal: true

require 'georuby'

module Hours
module Aggregates
# Generates (finds) an address for a lat:lon
class Address
ADDRESS_TABLE = :query_addresses
DISTANCE_MARGIN = 20

def initialize(geojson)
@geojson = Hashie.symbolize_keys(geojson)
end

def number
properties[:'addr:housenumber'] || address[:number]
end
alias housenumber number

def street
properties[:'addr:street'] || address[:street]
end

def city
properties[:'addr:city'] || address[:city]
end

def postcode
properties[:'addr:postcode'] || address[:postcode]
end

private

def properties
@geojson.fetch(:properties, {})
end

def dataset
Hours.address_database[ADDRESS_TABLE]
end

def address
return @address if @address

# Fallback to setting the memoized @address to empty hash to avoid
# re-looking up a nonexisting address
@address = dataset.order(sql_distance).first || {}
end

def point
@point ||= GeoRuby::SimpleFeatures::Point.from_xy(
*@geojson.fetch(:geometry, {}).fetch(:coordinates, [0, 0])
)
end

def sql_distance
Sequel.lit('location <-> ?', point)
end
end
end
end

+ 1
- 23
app/aggregates/place/region.rb View File

@@ -1,14 +1,11 @@
# frozen_string_literal: true

require 'offline_geocoder'
require 'babosa'

module Hours
module Aggregates
# Generates a normalized, distinguishable region string.
class Region
attr_writer :offline_geocoder

def initialize(geojson)
@geojson = Hashie.symbolize_keys(geojson)
end
@@ -18,26 +15,7 @@ module Hours
end

def city
@geojson.fetch(:properties, {})
.fetch(:'addr:city', region_from_geom)
end

private

def lat
@geojson.fetch(:geometry, {}).fetch(:coordinates)[1]
end

def lon
@geojson.fetch(:geometry, {}).fetch(:coordinates)[0]
end

def region_from_geom
offline_geocoder.search(lat, lon)[:name]
end

def offline_geocoder
@offline_geocoder ||= OfflineGeocoder.new
@geojson.fetch(:properties, {}).fetch(:'addr:city', '') || ''
end
end
end

+ 9
- 1
app/models/place_index.rb View File

@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'georuby'

require_relative 'base_collection.rb'
require_relative 'place.rb'

@@ -22,7 +24,13 @@ module Hours

def initialize(dataset, paginator = nil, centerpoint = nil)
super(dataset, paginator)
@centerpoint = centerpoint
@centerpoint = centerpoint || null_island
end

private

def null_island
GeoRuby::SimpleFeatures::Point.from_xy(0, 0)
end
end
end

+ 1
- 0
app/projections/query.rb View File

@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'pagy'
require 'offline_geocoder'

module Hours
module Projections

+ 34
- 0
bin/openaddr2copy View File

@@ -0,0 +1,34 @@
#!/bin/bash

# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
# set -o xtrace

# Assuming a table like this:
# CREATE TABLE query_addresses (
# hash char(16) PRIMARY KEY,
# number varchar(32),
# street varchar(255),
# unit varchar(255),
# city varchar(255),
# district varchar(255),
# region varchar(255),
# postcode varchar(32),
# location GEOMETRY(POINT, 4326)
# );
# CREATE INDEX query_address_location ON query_addresses USING GIST(location);
# It can be inserted into postgres using:
# ./bin/openaddr < openaddr/nl/countrywide.csv | psql $DB_URL -c "COPY query_addresses FROM STDIN DELIMITER AS ',' CSV HEADER"

# Print the new header
csvtk rename -f1-11 -n'lon,lat,number,street,unit,city,district,region,postcode,id,hash' - \
| csvtk uniq -f hash \
| csvtk mutate2 -n location -e '"SRID=4326;POINT(" + $lon + " " + $lat + ")"' \
| csvtk cut -f hash,number,street,unit,city,district,region,postcode,location

+ 28
- 0
bin/seedaddr View File

@@ -0,0 +1,28 @@
#!/bin/bash

# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
#set -o xtrace

source .env
APP_ENV=${APP_ENV:=development}
ENV_SRC_FILE=".env.${APP_ENV}"
if [ -f ${ENV_SRC_FILE} ]; then
source ${ENV_SRC_FILE}
fi
DATABASE_TABLE="query_addresses"

DB_URL="postgres://${DB_USER}:${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}"
function psql_exec {
echo $(psql $DB_URL -c "${1}")
}

psql_exec "TRUNCATE ${DATABASE_TABLE}"
psql_exec "COPY ${DATABASE_TABLE} FROM STDIN DELIMITER AS ',' CSV HEADER"

+ 0
- 278
data/country_name.sql
File diff suppressed because it is too large
View File


+ 6
- 0
lib/hours.rb View File

@@ -67,6 +67,12 @@ module Hours
EventSourcery::Postgres.config.projections_database
end

def self.address_database
# For now, we're lazy and stick the addresses in the same projections
# database. It could (and probably should) be a separate database.
EventSourcery::Postgres.config.projections_database
end

def self.repository
@repository ||= EventSourcery::Repository.new(
event_source: event_source,

+ 58
- 0
test/aggregates/place/address_test.rb View File

@@ -0,0 +1,58 @@
# frozen_string_literal: true

require 'test_helper'
require_relative Hours.base_path.join 'app/aggregates/place/address.rb'

##
# Tests the Address Builder/Finder.
#
# A unit-test that does go via the Database. This is by design, since
# this unit is self-contained and using its own database/table.
# When that database/table is part of the production database, that is
# "accidental", it might live somewhere on its own entirely. This address
# is a fully self-contained unit.
describe Hours::Aggregates::Address do
subject { Hours::Aggregates::Address.new(geojson) }

describe 'with empty address' do
let(:geojson) { json_fixtures('input/addressless.json') }

it '#street is set from database' do
assert_equal 'Stationsplein', subject.street
end

it '#number aliased housenumber is set from database' do
assert_equal '6P', subject.number
assert_equal '6P', subject.housenumber
end

it '#postcode is set from database' do
assert_equal '6512AB', subject.postcode
end

it '#city is set from database' do
assert_equal 'Nijmegen', subject.city
end
end

describe 'with preset address' do
let(:geojson) { json_fixtures('input/hm_burchtstraat.json') }

it '#street is taken from geojson' do
assert_equal 'Burchtstraat', subject.street
end

it '#number aliased housenumber is taken from geojson' do
assert_equal '1', subject.number
assert_equal '1', subject.housenumber
end

it '#postcode is taken from geojson' do
assert_equal '6511RA', subject.postcode
end

it '#city is taken from geojson' do
assert_equal 'Nijmegen', subject.city
end
end
end

+ 3
- 35
test/aggregates/place/region_test.rb View File

@@ -4,8 +4,6 @@ require 'test_helper'
require_relative Hours.base_path.join 'app/aggregates/place/region.rb'

describe Hours::Aggregates::Region do
let(:offline_geocoder) { Minitest::Mock.new }

subject do
Hours::Aggregates::Region.new(geojson)
end
@@ -38,39 +36,9 @@ describe Hours::Aggregates::Region do
end

describe 'without addr:city' do
let(:geojson) { json_fixtures('input/marienburg_bicycle.json') }

let(:lat) { geojson[:geometry][:coordinates][1] }
let(:lon) { geojson[:geometry][:coordinates][0] }

let(:bayswater) do
{
lat: 51.51116,
lon: -0.18426,
name: 'Bayswater',
admin1: 'England',
admin2: 'Greater London',
cc: 'GB',
country: 'United Kingdom'
}
end

it 'generates from offline_geocoder' do
assert_nil geojson[:properties][:'addr:city']
subject.offline_geocoder = offline_geocoder
offline_geocoder.expect(:search, bayswater, [lat, lon])

assert_equal 'bayswater', subject.region_slug
assert_mock offline_geocoder
end

it 'sets the city from offline_geocoder' do
assert_nil geojson[:properties][:'addr:city']
subject.offline_geocoder = offline_geocoder
offline_geocoder.expect(:search, bayswater, [lat, lon])

assert_equal 'Bayswater', subject.city
assert_mock offline_geocoder
let(:geojson) { json_fixtures('input/addressless.json') }
it 'is empty' do
assert_empty subject.region_slug
end
end
end

+ 21
- 14
test/aggregates/place_test.rb View File

@@ -11,6 +11,7 @@ describe Hours::Aggregates::Place do
let(:region_slug) { 'region' }
let(:city) { 'Region' }
let(:region_builder) { Minitest::Mock.new }
let(:address_builder) { Minitest::Mock.new }
subject { Hours::Aggregates::Place.new(aggregate_id, events) }

before do
@@ -18,8 +19,13 @@ describe Hours::Aggregates::Place do
subject.place_id_builder = place_id_builder

region_builder.expect(:region_slug, region_slug)
region_builder.expect(:city, city)
subject.region_builder = region_builder

address_builder.expect(:city, 'City')
address_builder.expect(:postcode, '')
address_builder.expect(:number, '')
address_builder.expect(:street, '')
subject.address_builder = address_builder
end

# This is implemented in EventSourcery::AggregateRoot, but it allows
@@ -33,30 +39,31 @@ describe Hours::Aggregates::Place do

it 'builds a place_id' do
place = subject.add(payload)
assert_equal place_id, place.place_id
assert_equal place_id, body_properties(place)['place_id']
assert_mock place_id_builder
end

it 'builds a region_slug' do
place = subject.add(payload)
assert_equal region_slug, place.region_slug
assert_equal region_slug, body_properties(place)['region_slug']
assert_mock region_builder
end

describe 'when city is empty' do
it 'sets the addr:city from region' do
place = subject.add(payload)
assert_equal 'Region', place.changes.first.body['properties']['addr:city']
assert_mock region_builder
describe 'address' do
# Test with :city. other attributes are the same
describe 'when addr:* is empty' do
it 'sets the addr:city from address_builder' do
place = subject.add(payload)
# The capitalized versions are what is mocked
assert_equal 'City', body_properties(place)['addr:city']
assert_mock address_builder
end
end
end

describe 'when city is not empty' do
let(:payload) { { properties: { 'addr:city' => 'City' } } }
private

it 'leaves the add:city alone' do
place = subject.add(payload)
assert_equal 'City', place.changes.first.body['properties']['addr:city']
end
def body_properties(place)
place.changes.first&.body&.fetch('properties', {})
end
end

+ 139
- 0
test/fixtures/address_sample.csv View File

@@ -0,0 +1,139 @@
hash,number,street,unit,city,district,region,postcode,location
4f51cd9959943379,1,Stationsplein,,Nijmegen,,Gelderland,6512AA,SRID=4326;POINT(5.8531012 51.8432072)
c251422e0a839ed6,2,Stationsplein,,Nijmegen,,Gelderland,6512AA,SRID=4326;POINT(5.8531021 51.8431853)
72e9b48bfa5d0fe7,3,Stationsplein,,Nijmegen,,Gelderland,6512AA,SRID=4326;POINT(5.8531012 51.8432072)
6acd89c350b12678,4,Stationsplein,,Nijmegen,,Gelderland,6512AA,SRID=4326;POINT(5.8531021 51.8431853)
ef65d50d55088d77,5,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8531012 51.8432072)
7bfd4ffb246dac49,6A,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
5460453af52d04c9,6B,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
4183f0828760f05a,6C,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
a51cb1b1ff847e6e,6D,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
a4f6b062fc6b6618,6E,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
09b012182bea9d04,6F,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
cdcac712ef4b9a3e,6G,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
bf2c3233331a7161,6H,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530907 51.8431625)
cc44d822987dffb2,6J,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530937 51.843142)
98b90479a1d78a28,6K,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530937 51.843142)
96a32ad7a9a66f23,6L,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530937 51.843142)
78165cfa2a48c3b1,6M,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853092 51.8431221)
9c69071c7e35432a,6N,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853092 51.8431221)
d9fd411a842dc32c,6P,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530919 51.843104)
e04b78380b77267a,6Q,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8530919 51.843104)
73bbf6cabc76da3d,6,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8531021 51.8431853)
21b90378c98ada6f,7,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8533866 51.8428513)
3af879e65f2c9c5f,8,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8525674 51.843689)
51957fdaf1001846,9K-1,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538009 51.8442869)
d949d08ff8d1c802,9K-10,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535038 51.8443288)
dd7debb04f353948,9K-11,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536123 51.8443035)
05dd78cc990409ef,9K-12,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536836 51.8442856)
3ad495bbd68c8c82,9K-13,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537581 51.8442959)
60b7f916f523966f,9K-14,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537865 51.8442763)
0805c3a34685590f,9K-15,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853555 51.8443038)
50b01dc182d9b503,9K-16,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535667 51.8443215)
063ce9413c0c410e,9K-17,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536836 51.8442856)
f6edc674881accd7,9K-18,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535836 51.8442966)
69ea91851509a6bd,9K-19,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538179 51.8442708)
7006261570217020,9K-2,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.85357 51.8443675)
afe6bd3a5b937ecd,9K-20,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538009 51.8442869)
fdf0afc5ddc3913d,9K-21,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537439 51.8443066)
c39ccc36dcf64098,9K-22,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538409 51.8442778)
8a5a059ddbed0725,9K-23,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538182 51.8442956)
02ff6d395c9705d3,9K-24,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8534836 51.8443147)
f8192b36ac197788,9K-25,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535207 51.8443039)
d632c385c4f16586,9K-26,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538295 51.8442832)
62ca38c51f95f17d,9K-27,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537008 51.844289)
7e296c463606021a,9K-28,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535377 51.8442826)
53d1cfb0cd431712,9K-29,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538436 51.8442707)
7c7b3eedb5513011,9K-3,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536123 51.8443035)
619fc98273cd0b2e,9K-30,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537238 51.8442996)
a488c5bcad4de6bb,9K-31,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535925 51.8443249)
aa5c67dee5ec6a9e,9K-32,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537896 51.8443028)
55384a29bdcfe3dc,9K-33,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537637 51.8442817)
c46f8ef627baf5bd,9K-34,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536351 51.8443035)
b6cacbbdf8e51682,9K-35,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536242 51.8443496)
92b48d37258e0aed,9K-36,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536155 51.8443443)
8f2b5412ae3a3634,9K-37,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538182 51.8442956)
facbd23fc8caffb4,9K-38,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536242 51.8443496)
bae92dfd81eb3d4b,9K-39,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536408 51.8442928)
d6891490fd92ae83,9K-4,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536179 51.8442929)
6374e218c5b4767b,9K-40,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537406 51.8442676)
0e97bc1abdc5b533,9K-41,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537778 51.8442657)
959203fd057874a5,9K-42,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536977 51.8442695)
b0466ba5b30842c1,9K-43,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853632 51.8442769)
fa1d3763c535cb18,9K-44,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535836 51.8442966)
e6f1d0484f6d901e,9K-45,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536781 51.8443068)
9ee9f445964d21f4,9K-46,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537351 51.8442871)
981299945cb2f94c,9K-47,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536381 51.8443141)
b96919d82b9eb1ec,9K-48,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853538 51.844318)
5ba8b3cd229c04d5,9K-49,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536953 51.8443068)
671470351b651c1d,9K-5,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536634 51.8442661)
9a8e285b521ebda3,9K-50,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536297 51.8443247)
338e90ce4b3682d4,9K-51,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853601 51.8443178)
94928916e34fbffa,9K-52,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536666 51.844298)
a5af9360c352e325,9K-53,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537463 51.8442587)
58c997ae56b5fdf3,9K-54,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537093 51.8442766)
f7890c6f4f36923d,9K-55,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536109 51.8442892)
9787abf7dcd3906a,9K-56,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535723 51.8443161)
05f9be2f967a7653,9K-57,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853661 51.8443104)
b99bb7b8b92404f8,9K-58,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535608 51.8443108)
468539c69c46d172,9K-59,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537262 51.844257)
7d096d8ff77a5ec3,9K-6,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537408 51.84428)
fbc88a7d57cbbc7e,9K-60,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853655 51.8442874)
7eedfa7ec1a9656a,9K-61,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537748 51.8442551)
da39ecfd70758847,9K-62,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537235 51.8442712)
e054f6ea80e22964,9K-63,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537037 51.8442943)
1b139a3ed1069d68,9K-64,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536124 51.8443142)
835066f8c31b8291,9K-65,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535659 51.8442909)
cdfa3e141e6eae86,9K-66,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535836 51.8442966)
49c9f859befbf655,9K-67,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536496 51.8443211)
944f91f034cb0bb8,9K-68,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853632 51.8442769)
e7ce9cdc8836221d,9K-69,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853555 51.8443038)
95b4b10c052130be,9K-7,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537865 51.8442763)
254a1a90f4d691a3,9K-70,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853755 51.8442764)
bfcfd7ad0b51e523,9K-71,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537865 51.8442763)
1144b9b208d58d4d,9K-72,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853598 51.8443089)
c23b6e31948685bd,9K-73,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536692 51.8442732)
880b20cfa6287dc0,9K-74,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536351 51.8443035)
34c0bbd7198918c7,9K-75,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537581 51.8442959)
2eaf2657a4eddeb1,9K-76,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853584 51.8443391)
dac310ec154861c5,9K-8,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853584 51.8443391)
a0e00274c01ca72a,9K-81,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535955 51.8443426)
d10608b051d8c1cf,9K-82,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537896 51.8443028)
8660377730b8cfe6,9K-83,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535297 51.8443411)
d48efad5fa782e4e,9K-84,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538436 51.8442707)
6edb6ad0ae566fe6,9K-85,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8538493 51.8442601)
f7b4f962ac1a0369,9K-86,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535526 51.8443392)
2636f161fd5ced45,9K-9,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537408 51.84428)
d7ea23630f58ddfa,9K-91,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537213 51.8443332)
228477bc6ac67140,9K-92,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.853481 51.8443324)
45ac0478e42acb38,9K-93,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537896 51.8443028)
fa7356bb1a0012d3,9K-94,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535094 51.8443111)
8cda14f46ec8992d,9K-95,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537896 51.8443028)
eddf48c0cd5f1db7,9K-96,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8537351 51.8442871)
fc12a8650b983f9f,9,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535561 51.8442924)
9df66d70faf83ff7,11A,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8534453 51.8443322)
1c3ad562bcf34bbe,11B,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8535435 51.8446206)
477f665e07280f71,11C,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8536823 51.8443356)
c364ea98a2080f25,11,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8541368 51.8442635)
1c03cd2fd8bd2c51,12,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
5bc3995e71924521,13,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
1e37f69e1f8b2ef8,14,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
80b29de86872282e,15,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
2bde8b2c3fc4848a,16,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
39accf64021ea328,17,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
aa828f7123b05f60,18,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
066c1380f4187f57,19,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
c5ee2e85114529b1,20,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
ced4139cb2617f5a,21,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
577e2c0befc61025,22,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544949 51.8430769)
2e300cee5970a2d7,23,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8544964 51.8430573)
b4b2a739fca5c00e,26,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8541473 51.842218)
7ca7589bb344c762,29,Stationsplein,,Nijmegen,,Gelderland,6512AB,SRID=4326;POINT(5.8529342 51.8420089)
2215e111e1ac5ce8,26,Vijzelstraat,,Arnhem,,Gelderland,6811ET,SRID=4326;POINT(5.9067795 51.9815167)
b4ac426074553d7c,2,Op het Toneel,,Arnhem,,Gelderland,6836ND,SRID=4326;POINT(5.8939416 51.9418593)
adb19e7d67038baa,3,Op het Toneel,,Arnhem,,Gelderland,6836ND,SRID=4326;POINT(5.8941034 51.9415266)
2b305bcfe358a429,4,Op het Toneel,,Arnhem,,Gelderland,6836ND,SRID=4326;POINT(5.8940051 51.9416768)
ef89c57948db291d,3,Arsenaalgas,,Nijmegen,,Gelderland,6511PB,SRID=4326;POINT(5.8664921 51.8448285)
847f8daacc86291f,7A,Arsenaalgas,,Nijmegen,,Gelderland,6511PB,SRID=4326;POINT(5.8661005 51.8446562)
a2fc8c0a642409e8,7,Arsenaalgas,,Nijmegen,,Gelderland,6511PB,SRID=4326;POINT(5.8658718 51.8447315)
eaa13cc029788f00,8,Arsenaalgas,,Nijmegen,,Gelderland,6511PE,SRID=4326;POINT(5.865782 51.8447778)

+ 8
- 4
test/fixtures/input/addressless.json View File

@@ -1,11 +1,15 @@
{
"type": "Feature",
"geometry": {
"id": "2303136888",
"geometry": {
"type": "Point",
"coordinates": [5.8653234, 51.8473397]
"coordinates": [
5.8531038,
51.8429894
]
},
"properties": {
"name": "Sint-Nicolaaskapel",
"opening_hours": "24/7; PH closed"
"amenity": "fast_food",
"name": "Smullers"
}
}

+ 9
- 9
test/fixtures/input/hm_arnhem.json View File

@@ -9,19 +9,19 @@
"type" : "Feature",
"id" : "2725646258",
"properties" : {
"addr:postcode" : "6811ET",
"addr:city" : "Arnhem",
"name" : "H&M",
"opening_hours" : "Mo 11:00-18:00; Tu-We 10:00-18:00; Th 10:00-21:00; Fr 10:00-18:00; Sa 09:30-18:00",
"shop" : "clothes",
"source:date" : "2014-02-11",
"brand:wikipedia" : "en:H&M",
"shop:number" : "NL0076",
"brand" : "H&M",
"addr:postcode" : "6811ET",
"addr:city" : "Arnhem",
"addr:housenumber" : "26",
"brand:wikidata" : "Q188326",
"source" : "BAG;http://www.hm.com/nl/",
"addr:street" : "Vijzelstraat",
"addr:country" : "NL",
"name" : "H&M"
"source" : "BAG;http://www.hm.com/nl/",
"brand" : "H&M",
"source:date" : "2014-02-11",
"brand:wikipedia" : "en:H&M",
"brand:wikidata" : "Q188326",
"shop:number" : "NL0076"
}
}

+ 33
- 0
test/integration/api/add_places_test.rb View File

@@ -75,6 +75,39 @@ describe 'add place' do
end
end

describe 'without an address' do
let(:payload) { json_fixtures('input/addressless.json') }

before do
header('Accept', 'application/json')
%i[street housenumber postcode city country].each do |attr|
full_attr = :"addr:#{attr.to_s}"
assert_nil payload[:properties][full_attr]
end
end

it 'adds an address from the geometry point' do
post_json '/places', payload
assert_status(201)
projector_process_event(id_from_header)

get "/places/#{id_from_header}"
expected = {
data: {
attributes: {
address: {
postcode: '6512AB',
city: 'Nijmegen',
housenumber: '6P',
street: 'Stationsplein'
}
}.ignore_extra_keys!
}.ignore_extra_keys!
}
assert_json_match expected, last_response.body
end
end

describe 'with a missing geometry' do
it 'returns bad request for missting missing geometry' do
payload_missing_geom = payload.slice(:type, :id, :properties)

+ 3
- 1
test/integration/api/view_places_test.rb View File

@@ -102,7 +102,9 @@ describe 'api view places' do
describe 'GET places/:place_id' do
it 'forwards client with 303 to place by UUID' do
place = Hours::Aggregates::Place.new(hm_id, [])
place_id = place.add(json_fixtures('input/hm_burchtstraat.json')).place_id
# Extract the place_id from the place aggregate
changes = place.add(json_fixtures('input/hm_burchtstraat.json')).changes
place_id = changes.first.body['properties']['place_id']

get "/places/#{place_id}"
assert_status(303)

+ 5
- 0
test/models/place_index_test.rb View File

@@ -19,4 +19,9 @@ describe Hours::Models::PlaceIndex do
subject = Hours::Models::PlaceIndex.new(dataset, nil, centerpoint)
assert_equal centerpoint, subject.centerpoint
end

it 'has a default centerpoint' do
subject = Hours::Models::PlaceIndex.new(dataset, nil, nil)
assert_equal null_island, subject.centerpoint
end
end

+ 4
- 0
test/support/location_helpers.rb View File

@@ -7,6 +7,10 @@ module LocationHelpers

DISTANCE_DELTA = 0.001 # Translates to 79-111 meter.

def null_island
GeoRuby::SimpleFeatures::Point.from_xy(0, 0)
end

def assert_nearby(expected, actual, delta = DISTANCE_DELTA, message = nil)
[0, 1].each do |index|
vector = index.zero? ? 'lat' : 'lon'

+ 2
- 1
test/test_helper.rb View File

@@ -35,7 +35,8 @@ module Minitest
config.logger = Logger.new(nil)
end

DatabaseCleaner.strategy = :truncation, { except: %w[spatial_ref_sys] }
DatabaseCleaner.strategy = :truncation,
{ except: %w[spatial_ref_sys query_addresses] }

before :each do
DatabaseCleaner.start

Loading…
Cancel
Save