A Geometry library for Elixir that calculates spatial relationships between two geometries
Elixir
173
44 commits
updated Jan 25, 2025
A Geometry library for Elixir that calculates spatial relationships between two geometries. Geometries can be of any of the following types:
defp deps do
[{:topo, "~> 1.0"}]
end
The Topo module provides functions for determining the relationship between
two geometries. Each function returns a boolean and accepts any combination of
Point, LineString, Polygon, MultiPoint, MultiLineString, or MultiPolygon.
intersects? - Geometries A and B share at least one point in
common.disjoint? - Disjoint geometries share no points in common. This is the
direct opposite of the intersects? result.contains? - All points of geometry B lie within A. See section
below on [Contains].within? - This is the direct inverse of contains?. All points of
geometry A lie within geometry B.equals? - Geometries A and B are equivalent and cover the exact
same set of points. By definition, A and B are equal if A contains
B and B contains A. Equality does not necessarily mean that the
geometries are of the same type. A Point A is equal to a MultiPoint that
contains only the same Point A.Each of these functions can be passed any two Geometries in either a Map with a
:type and :coordinates keys or as a struct generated via the Geo library. Coordinates are represented as atoms {x, y}
and multiple coordinates as Lists.
a = %{type: "Polygon", coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
b = %Geo.Polygon{coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
Topo.equals? a, b # => true
Instead of a Point geometry, just a single coordinate can be used.
a = %{type: "Polygon", coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
Topo.intersects? a, {4, 6} # => true
The Topo library's functions will automatically attempt to "clean" geometries
passed to them:
contains?There are a few non-obvious special cases that are worth mentioning:
a = %Geo.Polygon{coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
b = %Geo.LineString{coordinates: [{2, 2}, {20, 2}, {11, 11}, {2, 2}]}
Topo.contains? a, b # => false
Topo.intersects? a, b # => true
a = %Geo.LineString{coordinates: [{1, 3}, {2, -1}, {0, -1}]}
b = %Geo.LineString{coordinates: [{1, 3}, {2, -1}, {0, -1}, {1, 3}]}
Topo.contains? a, {1, 3} # => false
Topo.intersects? a, {1, 3} # => true
Topo.contains? b, {1, 3} # => true
It is possible that floating point math imprecision can cause incorrect results for certain inputs. This is often encountered during the line segment comparison (see LineStringPolygonTest for an example). By default, Topo is strict on intersection math; however, if you with to allow a less strict requirement for line segment intersection, you can set an :epsilon value at compile time, which will be passed to the SegSeg library (see here for a more detailed explanation).
In your application's config file add
config :topo, epsilon: true
Topo uses the Application.config_env/3 function to avoid querying the value on each computation, so you may have to clean and recompile the dependencies of your application after changing. The default value is false will will apply strict comparison to the resulting floating point numbers used in calculating line segment relationships.
> mix test
Elixir
100.0%
A Geometry library for Elixir that calculates spatial relationships between two geometries
Elixir
173
44 commits
updated Jan 25, 2025
A Geometry library for Elixir that calculates spatial relationships between two geometries. Geometries can be of any of the following types:
defp deps do
[{:topo, "~> 1.0"}]
end
The Topo module provides functions for determining the relationship between
two geometries. Each function returns a boolean and accepts any combination of
Point, LineString, Polygon, MultiPoint, MultiLineString, or MultiPolygon.
intersects? - Geometries A and B share at least one point in
common.disjoint? - Disjoint geometries share no points in common. This is the
direct opposite of the intersects? result.contains? - All points of geometry B lie within A. See section
below on [Contains].within? - This is the direct inverse of contains?. All points of
geometry A lie within geometry B.equals? - Geometries A and B are equivalent and cover the exact
same set of points. By definition, A and B are equal if A contains
B and B contains A. Equality does not necessarily mean that the
geometries are of the same type. A Point A is equal to a MultiPoint that
contains only the same Point A.Each of these functions can be passed any two Geometries in either a Map with a
:type and :coordinates keys or as a struct generated via the Geo library. Coordinates are represented as atoms {x, y}
and multiple coordinates as Lists.
a = %{type: "Polygon", coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
b = %Geo.Polygon{coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
Topo.equals? a, b # => true
Instead of a Point geometry, just a single coordinate can be used.
a = %{type: "Polygon", coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
Topo.intersects? a, {4, 6} # => true
The Topo library's functions will automatically attempt to "clean" geometries
passed to them:
contains?There are a few non-obvious special cases that are worth mentioning:
a = %Geo.Polygon{coordinates: [[{2, 2}, {20, 2}, {11, 11}, {2, 2}]]}
b = %Geo.LineString{coordinates: [{2, 2}, {20, 2}, {11, 11}, {2, 2}]}
Topo.contains? a, b # => false
Topo.intersects? a, b # => true
a = %Geo.LineString{coordinates: [{1, 3}, {2, -1}, {0, -1}]}
b = %Geo.LineString{coordinates: [{1, 3}, {2, -1}, {0, -1}, {1, 3}]}
Topo.contains? a, {1, 3} # => false
Topo.intersects? a, {1, 3} # => true
Topo.contains? b, {1, 3} # => true
It is possible that floating point math imprecision can cause incorrect results for certain inputs. This is often encountered during the line segment comparison (see LineStringPolygonTest for an example). By default, Topo is strict on intersection math; however, if you with to allow a less strict requirement for line segment intersection, you can set an :epsilon value at compile time, which will be passed to the SegSeg library (see here for a more detailed explanation).
In your application's config file add
config :topo, epsilon: true
Topo uses the Application.config_env/3 function to avoid querying the value on each computation, so you may have to clean and recompile the dependencies of your application after changing. The default value is false will will apply strict comparison to the resulting floating point numbers used in calculating line segment relationships.
> mix test
Elixir
100.0%