GeoMeasure - calculate properties of Geo structs

Hi Everyone,

I’d like to share my first open-source package and my first Elixir project, GeoMeasure. It enables one to calculate properties, such as area, perimeter, bounding box, extent, etc. of different Geo geometries. It might be of interest to those in the geospatial domain, as it allows easy property calculations without having to worry about extracting coordinates from your Geo structs, and instead just passing the whole item as a parameter to the functions.
Currently, the project is in its infancy, as I have just published it to Hex. It supports calculations with Geo.Point, Geo.LineString and Geo.Polygon for now, but I plan on eventually supporting all available geometries in the Geo package.
Making this package is a great learning experience for me and I hope it can be useful some members of the community, too. Please feel free to ask any question, and make suggestions :slight_smile:

Hex: geomeasure | Hex
HexDocs: GeoMeasure — GeoMeasure v0.0.1

The GitHub repository is available at the link below:

14 Likes

One day, you might be interested to marry it to geolocation, so I’d just drop a link to a geoloc library here: LibLatLon v0.8.0 — Documentation

3 Likes

A good addition to the ecosystem, thank you.

Does the library make any assumptions about whether the coordinates are {lat, lon} or {lon, lat}? Are different SRIDs supported?

3 Likes

The Geo lib takes care of that at least (I use it quite a bit).

3 Likes

Due to the way some properties are calculated, the package can only be used eith projected coordinate systems. Calculating these properties on an ellispoid requires different methods, but I hope I can expand to support those, too, in the near future.

The assumption is, at least for the functions that return geometries, is {lon, lat}, or more like {x,y}, since we are talking projected coordinates.

Thats great, thats what the polygons in tz_world are defined to be. Might be fun to use your library to calculate the area of different timezones (although I can’t image a real-world use for that).

Hi Everyone,

I have just published a new version (v1.0.0) of GeoMeasure, it contains some potentially breaking changes, which, I believe, significantly improve the integrity of the package. GeoMeasure can now handle nil values in input coordinates (throws ArgumentError), and it does not implement functions for geometries where the result does not exist (such as area of Geo.Point). It also encourages the use of the main module as the entry point, instead of the submodules. Overall, the documentation and the way to interface with the package are much clearer now. I hope you’ll find this an improvement in usability and I welcome any and all suggestions for future improvements and expansion.

Have fun using Elixir :slight_smile:

2 Likes

Sounds like a fun use case, probably there is some real-world use in it if one wants to research the area of time zones and the number of people inside them, as well as the land area. Might be good for some colonialism studies. But I doubt anyone who does that actually ventures in here :smiley:

Hi Everyone,

I’m working on adding support for geometries with Z values. I’d like to ask for some opinions on what’s more useful from a user viewpoint.

I am doing the bounding box algorithm for the LineStringZ geometry right now, and I am torn between creating a cuboid type of disregarding the z coordinates and just getting the 2D bounding box of the geometry. I feel like custom types wouldn’t be used too much, and most of the time I would not want to deal with a rectangular cuboid, but technically that is more of a proper answer than disregarding the z coordinates.

What do you all think, should it be a 2D bounding box or a bounding cuboid for features with Z values?

Btw this stands for any other geometry, like PolygonZ or LineStringZM, too.

1 Like

Hi Everyone,

Just a quick note that with the recently published v1.4.0, GeoMeasure enables the transfer of SRIDs from the input to the output in functions that return Geo structs. I think this is an important step for the usability of this package. With the current state of development, it can now function well for everyday tasks that require measuring the supported properties, with limitations explained in the readme of the package.

For future development angles, I am working on the support of Z values for more complex structs, which will be a larger piece of work, as it probably requires contributing to Geo with some PolygonZ and potentially PolygonZM implementations. I am also thinking about issues with handling coplanar and non-coplanar polygons for area calculations, which might be quite troublesome. Once I am happy with the single-geometry situation, I will move on to implementing the methods for multipart geometries, too.

For more information, feel free to check out the links I posted at the beginning of the thread.

3 Likes

Hi Everyone,

With v1.5.0 now published, the area and perimeter calculations support polygons with holes in them. The hole areas get subtracted from the outer ring’s area, while the hole perimeters get added to the outer ring perimeter.

Otherwise, future goals have not changed since the last update. Not sure when I’ll get around to them, but I have been looking into implementing the necessary structs in Geo for a while, so there might be some developments on that front. For the non-coplanar polygons, I am unsure if Elixir is good enough at number crunching to deal with the algorithms needed for it. Will have to do some further research into it.

Have fun coding in Elixir :slight_smile:

Has the Geo package considered supporting GeoPackage.

In the UK, the Ordnance Survey distribute a lot of great data in this format, which is an extension of SQLite with tables arranged in a particular schema.

You can open a GeoPackage database in Elixir, and can ready regular data like latitude and longitude, etc, but not the polygon data.

2 Likes

I am not sure, to be honest. I’m not in any position to comment on Geo’s trajectory as I am not part of their developer circle. GeoMeasure is an independent project that relies on Geo’s in-memory vector geometry structs. From what I know about the company now behind Geo, Felt, they are developing a web-based GIS system, so in their case the main focus is probably web-transferable inputs, like JSON, WKB, and WKT.

However, I think your idea is quite interesting overall. For SpatiaLite databases (and PostGIS) there is the geo_sql package, which might be good for either inspiration or potentially as a contribution target. GeoPackages can be opened as SQLite databases so it might just be a good solution. The developer @aseigo might be the best one to ask about future plans in terms of its direction.

I would be more than happy to talk more about this and get involved with development work, too, so interested to see what comes out of this, especially since I work in the UK and deal with many GeoPackages, too.

Both geo and geometry en/decode to/from standardized serialization formats, with the application actually doing the store/retrieve bits.

As GeoPackage is both the storage and the serialization format, I don’t think that the full GeoPackage format as a whole would be in scope, but the GPB blob format probably would be.

THAT said ..

I just published v1.2.0 of geo_sql with support for the three Geopackage functions SpatiaLite provides. This allows one to do things like:

from(g in Geopackage, where: g.id == 1, select: SpatiaLite.geom_from_gpb(g.shape))
        |> GeopackageRepo.one()
        |> QueryUtils.decode_geometry(GeopackageRepo)

with a schema such as:

defmodule GeoSQL.Test.Schema.Geopackage do
  use Ecto.Schema

  @primary_key false
  schema "gpkg_table" do
    field(:id, :integer, source: :OBJECTID)
    field(:name, :string)
    field(:shape, GeoSQL.Geometry, source: :Shape)
  end
end

… and get back a Geometry struct

There are still a number of annoyances, so this should only be considered a start, namely:

  • The schema field (in this case g.shape) does not automatically resolve to geometry, which is why there is the decode_geometry call in the query above. Not great! This is because the schema just sees the Shape column as a regular geometry field. Some means to mark them as Geopackage geometry would be needed. e.g. GeoSQL.GeopackageGeometry. Then the type extension could Do The Right Thing automagically.
  • No spatial indexes when querying Geopackage geoetry, as it isn’t stored in SpatiaLite’s format

It would still be nice to have de/encoding support right in the geometry library, as that would allow a rather more efficient code path.

I’ll give the GeopackageGeometry field type a go tomorrow and see how that goes. While it may not be optimal in terms of performance, it should make it very convenient to use with just:

from(g in Geopackage, where: g.id == 1) |> GeopackageRepo.one()

.. and get the whole schema back properly deserialized into Elixir structs.

2 Likes

That turned out to be quite a bit easier than expected. I released v1.3 of GeoSQL which includes a new Ecto type: GeoSQL.Geometry.Geopackage. This allows schemas to have Geometry fields backed by Geopackage blobs in Geopackage databases.

Currently not supported by PostGIS (not sure that would even make sense at this point), and there’s a runtime dependency on SpatiaLite, at least until the geometry library gets native support for serializing/deserializing the Geopackage binary format.

If anyone feels like contributing that to the geometry library, that’d be awesome, and maybe @asianfilm you could check out geo_sql v1.3 and see if it does enough to be useful to you?

Cheers!

2 Likes

Thanks for the answer @aseigo it’s really interesting and cool that you already added the Geopackage geometry type in GeoSQL. I’ll definitely look into contributing to geometry, would be a good learning experience. About the package, it looks like it has a lot of overlap with Geo, so might be a good idea to support in GeoMeasure, too, just to give people the option of what they’d rather use.

Hi Everyone,

I just released v1.6.0 of GeoMeasure, adding support for the bounding box calculations of LineStringZ and LineStringZM, as well as support for the bounding box, centroid, extent, and perimeter calculations for PolygonZ. The only thing missing for PolygonZ is the area calculation now. Not really sure if I should be supporting non-coplanar polygons there, so it might be a bit of a longer research stage before I come out with a solution there, as I don’t really want to introduce breaking changes later.

Cheers :slight_smile:

1 Like

Yeah, the two libs are servicing the same problem space: Elixir structs for geometries with spatial reference systems with WKT, WKB, GeoJSON serialization.

The main differences are geometry (currently) has more complete support for all Z/M variants and a different approach to GeoJSON, while being significantly faster and using ~0.5x the memory in seralizing/deserializing.

geo is significantly older and used in a lot of places, though.

So right now the option is between more complete with better performance, or more ubiquitous and maintained by a company.

For me, the performance and feature completeness is more important, but that’s certainly not universal. I’d love to see them merge, tbh.

The Z/M completeness is something that’s important for me, and more performance is never really a bad thing, so I’ll look into supporting geometry in the future, at least once I’m done with the actual functionality for the Geo structs. I guess as geometry gets more and more popular, it will likely pique the interest of the people maintaining Geo, too, so a merge probably won’t be off the table.

1 Like