Skip to content

Commit

Permalink
Moved spatial_properties to contrib, turned import warning to ImportE…
Browse files Browse the repository at this point in the history
…rror when shapely is not installed, relocated the tests, added neo4j 3.4.0 to travis to test spatial properties properly.
  • Loading branch information
aanastasiou committed Nov 20, 2018
1 parent 3b37b63 commit d42fb7b
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 63 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ env:
- NEO4J_VERSION="3.1.7"
- NEO4J_VERSION="3.2.9"
- NEO4J_VERSION="3.3.3"
- NEO4J_VERSION="3.4.0"
install:
- sudo apt-get update && sudo apt-get install oracle-java8-installer
- curl -L http://dist.neo4j.org/neo4j-community-$NEO4J_VERSION-unix.tar.gz | tar xz
Expand Down
9 changes: 0 additions & 9 deletions neomodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@
NormalizedProperty, RegexProperty, EmailProperty,
JSONProperty, ArrayProperty, UniqueIdProperty)


# If shapely is not installed, its import will fail and the spatial properties will not be available
try:
from .spatial_properties import (NeomodelPoint,PointProperty)
except ImportError:
sys.stderr.write('WARNING: Shapely not found on system, spatial capabilities will not be available.\n'
'If required, you can install Shapely via `pip install shapely`.')


__author__ = 'Robin Edwards'
__email__ = '[email protected]'
__license__ = 'MIT'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@
__author__ = "Athanasios Anastasiou"

import neo4j.v1
from shapely.geometry import Point as ShapelyPoint
from neomodel.properties import Property, validator

# If shapely is not installed, its import will fail and the spatial properties will not be available
try:
from shapely.geometry import Point as ShapelyPoint
except ImportError:
raise ImportError('NEOMODEL ERROR: Shapely not found. If required, you can install Shapely via '
'`pip install shapely`.')

from neomodel.properties import Property, validator

# Note: Depending on how Neo4J decides to handle the resolution of geographical points, these two
# private attributes might have to be updated in the future or removed altogether.
Expand Down
1 change: 1 addition & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def pytest_addoption(parser):
"""
parser.addoption("--resetdb", action="store_true", help = "Ensures that the database is clear prior to running tests for neomodel", default=False)


def pytest_sessionstart(session):
"""
Provides initial connection to the database and sets up the rest of the test suite
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

import neomodel
import neomodel.contrib.spatial_properties
import shapely
import pytest

Expand All @@ -30,7 +31,8 @@ def basic_type_assertions(ground_truth, tested_object, test_description, check_n
assert tested_object.srid == ground_truth.srid, \
'{} does not have the expected SRID({})'.format(test_description, ground_truth.srid)
assert len(tested_object) == len(ground_truth), \
'{} dimensionality mismatch. Expected {}, had {}'.format(len(ground_truth.coords), len(tested_object.coords))
'{} dimensionality mismatch. Expected {}, had {}'.format(len(ground_truth.coords),
len(tested_object.coords))
else:
assert isinstance(tested_object, type(ground_truth)), '{} did not return NeomodelPoint'.format(test_description)
assert tested_object.crs == ground_truth.crs, \
Expand All @@ -48,41 +50,41 @@ def test_coord_constructor():
"""

# Implicit cartesian point with coords
ground_truth_object = neomodel.NeomodelPoint((0.0, 0.0))
new_point = neomodel.NeomodelPoint((0.0, 0.0))
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0))
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0))
basic_type_assertions(ground_truth_object, new_point, "Implicit 2d cartesian point instantiation")

ground_truth_object = neomodel.NeomodelPoint((0.0, 0.0, 0.0))
new_point = neomodel.NeomodelPoint((0.0, 0.0, 0.0))
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0))
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0))
basic_type_assertions(ground_truth_object, new_point, "Implicit 3d cartesian point instantiation")

# Explicit geographical point with coords
ground_truth_object = neomodel.NeomodelPoint((0.0, 0.0), crs='wgs-84')
new_point = neomodel.NeomodelPoint((0.0, 0.0), crs='wgs-84')
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0), crs='wgs-84')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0), crs='wgs-84')
basic_type_assertions(ground_truth_object, new_point,
"Explicit 2d geographical point with tuple of coords instantiation")

ground_truth_object = neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
new_point = neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
basic_type_assertions(ground_truth_object, new_point,
"Explicit 3d geographical point with tuple of coords instantiation")

# Cartesian point with named arguments
ground_truth_object = neomodel.NeomodelPoint(x=0.0, y=0.0)
new_point = neomodel.NeomodelPoint(x=0.0, y=0.0)
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint(x=0.0, y=0.0)
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(x=0.0, y=0.0)
basic_type_assertions(ground_truth_object, new_point, "Cartesian 2d point with named arguments")

ground_truth_object = neomodel.NeomodelPoint(x=0.0, y=0.0, z=0.0)
new_point = neomodel.NeomodelPoint(x=0.0, y=0.0, z=0.0)
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint(x=0.0, y=0.0, z=0.0)
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(x=0.0, y=0.0, z=0.0)
basic_type_assertions(ground_truth_object, new_point, "Cartesian 3d point with named arguments")

# Geographical point with named arguments
ground_truth_object = neomodel.NeomodelPoint(longitude=0.0, latitude=0.0)
new_point = neomodel.NeomodelPoint(longitude=0.0, latitude=0.0)
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint(longitude=0.0, latitude=0.0)
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(longitude=0.0, latitude=0.0)
basic_type_assertions(ground_truth_object, new_point, "Geographical 2d point with named arguments")

ground_truth_object = neomodel.NeomodelPoint(longitude=0.0, latitude=0.0, height=0.0)
new_point = neomodel.NeomodelPoint(longitude=0.0, latitude=0.0 ,height=0.0)
ground_truth_object = neomodel.contrib.spatial_properties.NeomodelPoint(longitude=0.0, latitude=0.0, height=0.0)
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(longitude=0.0, latitude=0.0 ,height=0.0)
basic_type_assertions(ground_truth_object, new_point, "Geographical 3d point with named arguments")


Expand All @@ -95,21 +97,21 @@ def test_copy_constructors():
# Instantiate from Shapely point

# Implicit cartesian from shapely point
ground_truth = neomodel.NeomodelPoint((0.0, 0.0), crs='cartesian')
ground_truth = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0), crs='cartesian')
shapely_point = shapely.geometry.Point((0.0, 0.0))
new_point = neomodel.NeomodelPoint(shapely_point)
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(shapely_point)
basic_type_assertions(ground_truth, new_point, 'Implicit cartesian by shapely Point')

# Explicit geographical by shapely point
ground_truth = neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
ground_truth = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
shapely_point = shapely.geometry.Point((0.0, 0.0, 0.0))
new_point = neomodel.NeomodelPoint(shapely_point, crs='wgs-84-3d')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(shapely_point, crs='wgs-84-3d')
basic_type_assertions(ground_truth, new_point, 'Explicit geographical by shapely Point')

# Copy constructor for NeomodelPoints
ground_truth = neomodel.NeomodelPoint((0.0, 0.0))
other_neomodel_point = neomodel.NeomodelPoint((0.0, 0.0))
new_point = neomodel.NeomodelPoint(other_neomodel_point)
ground_truth = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0))
other_neomodel_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0))
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(other_neomodel_point)
basic_type_assertions(ground_truth, new_point, 'NeomodelPoint copy constructor')


Expand All @@ -121,23 +123,26 @@ def test_prohibited_constructor_forms():
"""
# Absurd CRS
with pytest.raises(ValueError, message='Expected ValueError("Invalid CRS...")'):
new_point = neomodel.NeomodelPoint((0,0), crs='blue_hotel')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0,0), crs='blue_hotel')

# Absurd coord dimensionality
with pytest.raises(ValueError, message='Expected ValueError("Invalid vector dimensions...")'):
new_point = neomodel.NeomodelPoint((0,0,0,0,0,0,0), crs='cartesian')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0,0,0,0,0,0,0), crs='cartesian')

# Absurd datatype passed to copy constructor
with pytest.raises(TypeError, message='Expected TypeError("Invalid object passed to copy constructor...")'):
new_point = neomodel.NeomodelPoint('it don''t mean a thing if it ain''t got that swing', crs='cartesian')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint('it don''t mean a thing if it '
'ain''t got that swing', crs='cartesian')

# Trying to instantiate a point with any of BOTH x,y,z or longitude, latitude, height
with pytest.raises(ValueError, message='Expected ValueError("Invalid instantiation via arguments...")'):
new_point = neomodel.NeomodelPoint(x=0.0, y=0.0, longitude=0.0, latitude=2.0, height=-2.0, crs='cartesian')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint(x=0.0, y=0.0,
longitude=0.0, latitude=2.0, height=-2.0,
crs='cartesian')

# Trying to instantiate a point with absolutely NO parameters
with pytest.raises(ValueError, message='Expected ValueError("Invalid instantiation via no arguments...")'):
new_point = neomodel.NeomodelPoint()
new_point = neomodel.contrib.spatial_properties.NeomodelPoint()


def test_property_accessors_depending_on_crs():
Expand All @@ -147,7 +152,7 @@ def test_property_accessors_depending_on_crs():
:return:
"""
# Geometrical points only have x,y,z coordinates
new_point = neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='cartesian')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='cartesian')
with pytest.raises(AttributeError, message='Expected AttributeError("Invalid coordinate(''longitude'')...")'):
new_point.longitude
with pytest.raises(AttributeError, message='Expected AttributeError("Invalid coordinate(''latitude'')...")'):
Expand All @@ -156,7 +161,7 @@ def test_property_accessors_depending_on_crs():
new_point.height

# Geographical points only have longitude, latitude, height coordinates
new_point = neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d')
with pytest.raises(AttributeError, message='Expected AttributeError("Invalid coordinate(''x'')...")'):
new_point.x
with pytest.raises(AttributeError, message='Expected AttributeError("Invalid coordinate(''y'')...")'):
Expand All @@ -172,13 +177,13 @@ def test_property_accessors():
:return:
"""
# Geometrical points
new_point = neomodel.NeomodelPoint((0.0, 1.0, 2.0), crs='cartesian-3d')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 1.0, 2.0), crs='cartesian-3d')
assert new_point.x == 0.0, 'Expected x coordinate to be 0.0'
assert new_point.y == 1.0, 'Expected y coordinate to be 1.0'
assert new_point.z == 2.0, 'Expected z coordinate to be 2.0'

# Geographical points
new_point = neomodel.NeomodelPoint((0.0, 1.0, 2.0), crs='wgs-84-3d')
new_point = neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 1.0, 2.0), crs='wgs-84-3d')
assert new_point.longitude == 0.0, 'Expected longitude to be 0.0'
assert new_point.latitude == 1.0, 'Expected latitude to be 1.0'
assert new_point.height == 2.0, 'Expected height to be 2.0'
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import neomodel
import neomodel.contrib.spatial_properties
import pytest
import neo4j.v1
from .test_spatial_datatypes import basic_type_assertions
Expand All @@ -18,13 +19,13 @@ def test_spatial_point_property():
:return:
"""
with pytest.raises(ValueError, message='Expected ValueError("Invalid CRS (CRS not specified)")'):
a_point_property = neomodel.PointProperty()
a_point_property = neomodel.contrib.spatial_properties.PointProperty()

with pytest.raises(ValueError, message='Expected ValueError("Invalid CRS (CRS not acceptable)")'):
a_point_property = neomodel.PointProperty(crs='crs_isaak')
a_point_property = neomodel.contrib.spatial_properties.PointProperty(crs='crs_isaak')

with pytest.raises(TypeError, message='Expected TypeError("Invalid default value")'):
a_point_property = neomodel.PointProperty(default=(0.0, 0.0), crs='cartesian')
a_point_property = neomodel.contrib.spatial_properties.PointProperty(default=(0.0, 0.0), crs='cartesian')


def test_inflate():
Expand All @@ -49,10 +50,10 @@ def test_inflate():

# Run the above tests
for a_value in values_from_db:
expected_point = neomodel.NeomodelPoint(tuple(a_value[0]),
crs=neomodel.spatial_properties.SRID_TO_CRS[a_value[0].srid])
inflated_point = neomodel.PointProperty(crs=neomodel.spatial_properties.SRID_TO_CRS[a_value[0].srid]).inflate(
a_value[0])
expected_point = neomodel.contrib.spatial_properties.NeomodelPoint(tuple(a_value[0]),
crs=neomodel.contrib.spatial_properties.SRID_TO_CRS[a_value[0].srid])
inflated_point = neomodel.contrib.spatial_properties.PointProperty(
crs=neomodel.contrib.spatial_properties.SRID_TO_CRS[a_value[0].srid]).inflate(a_value[0])
basic_type_assertions(expected_point, inflated_point, '{}, received {}'.format(a_value[1], inflated_point))


Expand All @@ -63,22 +64,22 @@ def test_deflate():
"""
# Please see inline comments in `test_inflate`. This test function is 90% to that one with very minor differences.
#
CRS_TO_SRID = dict([(value, key) for key, value in neomodel.spatial_properties.SRID_TO_CRS.items()])
CRS_TO_SRID = dict([(value, key) for key, value in neomodel.contrib.spatial_properties.SRID_TO_CRS.items()])
# Values to construct and expect during deflation
values_from_neomodel = [(neomodel.NeomodelPoint((0.0, 0.0), crs='cartesian'),
values_from_neomodel = [(neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0), crs='cartesian'),
'Expected Neo4J 2d cartesian point when deflating Neomodel 2d cartesian point'),
(neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='cartesian-3d'),
(neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='cartesian-3d'),
'Expected Neo4J 3d cartesian point when deflating Neomodel 3d cartesian point'),
(neomodel.NeomodelPoint((0.0,0.0), crs='wgs-84'),
(neomodel.contrib.spatial_properties.NeomodelPoint((0.0,0.0), crs='wgs-84'),
'Expected Neo4J 2d geographical point when deflating Neomodel 2d geographical point'),
(neomodel.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d'),
(neomodel.contrib.spatial_properties.NeomodelPoint((0.0, 0.0, 0.0), crs='wgs-84-3d'),
'Expected Neo4J 3d geographical point when deflating Neomodel 3d geographical point')]

# Run the above tests.
for a_value in values_from_neomodel:
expected_point = neo4j.v1.spatial.Point(tuple(a_value[0].coords[0]))
expected_point.srid = CRS_TO_SRID[a_value[0].crs]
deflated_point = neomodel.PointProperty(crs=a_value[0].crs).deflate(a_value[0])
deflated_point = neomodel.contrib.spatial_properties.PointProperty(crs=a_value[0].crs).deflate(a_value[0])
basic_type_assertions(expected_point, deflated_point, '{}, received {}'.format(a_value[1], deflated_point),
check_neo4j_points=True)

Expand All @@ -90,22 +91,23 @@ def test_default_value():
"""

def get_some_point():
return neomodel.NeomodelPoint((random.random(),random.random()))
return neomodel.contrib.spatial_properties.NeomodelPoint((random.random(),random.random()))

class LocalisableEntity(neomodel.StructuredNode):
"""
A very simple entity to try out the default value assignment.
"""
identifier = neomodel.UniqueIdProperty()
location = neomodel.PointProperty(crs='cartesian', default=get_some_point)
location = neomodel.contrib.spatial_properties.PointProperty(crs='cartesian', default=get_some_point)

# Save an object
an_object = LocalisableEntity().save()
coords = an_object.location.coords[0]
# Retrieve it
retrieved_object = LocalisableEntity.nodes.get(identifier=an_object.identifier)
# Check against an independently created value
assert retrieved_object.location == neomodel.NeomodelPoint(coords), "Default value assignment failed."
assert retrieved_object.location == neomodel.contrib.spatial_properties.NeomodelPoint(coords), \
"Default value assignment failed."


def test_array_of_points():
Expand All @@ -120,12 +122,15 @@ class AnotherLocalisableEntity(neomodel.StructuredNode):
A very simple entity with an array of locations
"""
identifier = neomodel.UniqueIdProperty()
locations = neomodel.ArrayProperty(neomodel.PointProperty(crs='cartesian'))
locations = neomodel.ArrayProperty(neomodel.contrib.spatial_properties.PointProperty(crs='cartesian'))

an_object = AnotherLocalisableEntity(locations=
[neomodel.NeomodelPoint((0.0,0.0)), neomodel.NeomodelPoint((1.0,0.0))]).save()
[neomodel.contrib.spatial_properties.NeomodelPoint((0.0,0.0)),
neomodel.contrib.spatial_properties.NeomodelPoint((1.0,0.0))]).save()

retrieved_object = AnotherLocalisableEntity.nodes.get(identifier=an_object.identifier)

assert type(retrieved_object.locations) is list, "Array of Points definition failed."
assert retrieved_object.locations == [neomodel.NeomodelPoint((0.0,0.0)), neomodel.NeomodelPoint((1.0,0.0))], \
assert retrieved_object.locations == [neomodel.contrib.spatial_properties.NeomodelPoint((0.0,0.0)),
neomodel.contrib.spatial_properties.NeomodelPoint((1.0,0.0))], \
"Array of Points incorrect values."

0 comments on commit d42fb7b

Please sign in to comment.