.. _library_geospatial:

``geospatial``
==============

This library provides a ``geospatial_protocol`` protocol and a
``geospatial`` object for common geographic computations over
coordinates represented as ``geographic(Latitude,Longitude)``. By
default, distances are returned in kilometers.

API documentation
-----------------

Open the
`../../apis/library_index.html#geospatial <../../apis/library_index.html#geospatial>`__
link in a web browser.

Loading
-------

To load all entities in this library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(geospatial(loader)).

Testing
-------

To test this library predicates, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(geospatial(tester)).

Available predicates
--------------------

The library currently includes predicates for:

- Coordinate validation
- Coordinate normalization
- Point-to-point distances (``haversine`` and ``vincenty``)
- Rhumb-line distance, bearing, and destination predicates
- Rhumb-line interpolation and midpoint
- Generic distance dispatch (``distance/4`` and ``distance/5``)
- Initial and final bearings
- Midpoint and destination point computations
- Great-circle interpolation and track-distance
- Proximity checks and nearest coordinate search
- Mean center and coordinates bounding boxes
- Minimum enclosing circle
- Coordinates-to-bounding-box conversion
- Equirectangular projection and inverse
- Point-in-polygon checks
- Polygon area, polygon centroid, and polygon bounding boxes
- Polygon closure and orientation
- Polygon orientation normalization
- Polygon validity checks
- Bounding-box utilities
- Strict bounding-box overlap checks
- Bounding-box and polygon/polyline relation
- Nearest-point and point-to-polyline distance
- Polyline length and polygon perimeter
- Polyline simplification by tolerance
- Polyline split and resampling
- Polygon intersection checks
- Spherical bounding boxes
- Route distance accumulation (``route_distance/2``,
  ``route_distance/3``, and ``route_distance/4``)

For explicit units, predicates ``distance/5`` and ``route_distance/4``
support ``kilometers``, ``meters``, ``miles``, and ``nautical_miles``.

Generic metric dispatch accepts ``rhumb`` (and alias ``loxodrome``) in
addition to existing spherical and ellipsoidal metrics.

Notes
-----

The generic vector distance predicates already available in the
``types`` library (notably in the ``listp`` and ``numberlistp``
protocols) remain the recommended choice for non-geographic
n-dimensional data.

Usage
-----

Load the library:

::

   | ?- logtalk_load(geospatial(loader)).

Validate a coordinate:

::

   | ?- geospatial::valid_coordinate(geographic(38.7223, -9.1393)).

Normalize coordinates and convert to/from local planar coordinates:

::

   | ?- geospatial::normalize_coordinate(geographic(95.0, 10.0), Normalized).
   | ?- geospatial::equirectangular_projection(geographic(38.7223, -9.1393), 38.0, X, Y).
   | ?- geospatial::equirectangular_inverse(X, Y, 38.0, Coordinate).

Compute default distance in kilometers (Haversine):

::

   | ?- geospatial::distance(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), haversine, Distance).
   | ?- geospatial::rhumb_distance(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), Distance).
   | ?- geospatial::rhumb_bearing(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), Bearing).
   | ?- geospatial::rhumb_destination_point(geographic(38.7223, -9.1393), 45.0, 50.0, Destination).
   | ?- geospatial::interpolate_rhumb(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), 0.5, Intermediate).
   | ?- geospatial::rhumb_midpoint(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), Midpoint).

Compute distance with explicit unit (``meters``, ``miles``, or
``nautical_miles``):

::

   | ?- geospatial::distance(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), vincenty, miles, Distance).

Compute route distance using the default metric (``haversine``) in
kilometers:

::

   | ?- geospatial::route_distance([geographic(38.7223, -9.1393), geographic(39.7440, -8.8070), geographic(41.1579, -8.6291)], Distance).

Compute route distance with explicit metric and unit:

::

   | ?- geospatial::route_distance([geographic(38.7223, -9.1393), geographic(39.7440, -8.8070), geographic(41.1579, -8.6291)], vincenty, nautical_miles, Distance).

Compute midpoint and destination point:

::

   | ?- geospatial::midpoint(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), Midpoint).
   | ?- geospatial::destination_point(geographic(38.7223, -9.1393), 45.0, 50.0, Destination).
   | ?- geospatial::interpolate_great_circle(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), 0.5, Intermediate).
   | ?- geospatial::cross_track_distance(geographic(40.0, -9.0), geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), CrossTrackKm).
   | ?- geospatial::along_track_distance(geographic(40.0, -9.0), geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), AlongTrackKm).

Compute final bearing and proximity checks:

::

   | ?- geospatial::final_bearing(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), Bearing).
   | ?- geospatial::within_distance(geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), 300.0, haversine).

Find the nearest coordinate from a list:

::

   | ?- geospatial::nearest_coordinate(geographic(38.7223, -9.1393), [geographic(37.7749, -122.4194), geographic(41.1579, -8.6291), geographic(40.4168, -3.7038)], vincenty, Nearest, Distance).

Compute center and coordinate-list bounding boxes:

::

   | ?- geospatial::mean_center([geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), geographic(40.4168, -3.7038)], Center).
   | ?- geospatial::minimum_enclosing_circle([geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), geographic(40.4168, -3.7038)], Center, Radius).
   | ?- geospatial::coordinates_bounding_box([geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), geographic(40.4168, -3.7038)], BoundingBox).
   | ?- geospatial::bbox_from_coordinates([geographic(38.7223, -9.1393), geographic(41.1579, -8.6291), geographic(40.4168, -3.7038)], BoundingBox).

Work with polygons:

::

   | ?- geospatial::point_in_polygon(geographic(0.5, 0.5), [geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)]).
   | ?- geospatial::polygon_area([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)], Area).
   | ?- geospatial::polygon_centroid([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)], Centroid).
   | ?- geospatial::polygon_bounding_box([geographic(1.0, -2.0), geographic(0.0, 1.0), geographic(-1.0, -1.0)], BoundingBox).
   | ?- geospatial::close_polygon([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 0.0)], ClosedPolygon).
   | ?- geospatial::polygon_orientation([geographic(0.0, 0.0), geographic(1.0, 0.0), geographic(1.0, 1.0), geographic(0.0, 1.0)], Orientation).
   | ?- geospatial::is_clockwise_polygon([geographic(0.0, 0.0), geographic(1.0, 0.0), geographic(1.0, 1.0), geographic(0.0, 1.0)]).
   | ?- geospatial::normalize_polygon_orientation([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)], clockwise, Oriented).
   | ?- geospatial::clockwise_polygon([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)], Clockwise).
   | ?- geospatial::counterclockwise_polygon([geographic(0.0, 0.0), geographic(1.0, 0.0), geographic(1.0, 1.0), geographic(0.0, 1.0)], Counterclockwise).
   | ?- geospatial::is_valid_polygon([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 0.0)]).

Work with bounding boxes:

::

   | ?- geospatial::bbox_contains(bbox(geographic(-1.0, -1.0), geographic(1.0, 1.0)), geographic(0.5, 0.5)).
   | ?- geospatial::bbox_contains(bbox(geographic(-1.0, 170.0), geographic(1.0, -170.0)), bbox(geographic(-0.5, 175.0), geographic(0.5, -175.0))).
   | ?- geospatial::bbox_intersects(bbox(geographic(0.0, 0.0), geographic(1.0, 1.0)), bbox(geographic(0.5, 0.5), geographic(2.0, 2.0))).
   | ?- geospatial::bbox_overlaps(bbox(geographic(0.0, 0.0), geographic(1.0, 1.0)), bbox(geographic(0.5, 0.5), geographic(2.0, 2.0))).
   | ?- geospatial::bbox_intersects_polygon(bbox(geographic(0.0, 0.0), geographic(1.0, 1.0)), [geographic(0.5, 0.5), geographic(0.5, 1.5), geographic(1.5, 1.5), geographic(1.5, 0.5)]).
   | ?- geospatial::bbox_contains_polygon(bbox(geographic(0.0, 0.0), geographic(2.0, 2.0)), [geographic(0.5, 0.5), geographic(0.5, 1.5), geographic(1.5, 1.5), geographic(1.5, 0.5)]).
   | ?- geospatial::bbox_intersects_polyline(bbox(geographic(0.0, 0.0), geographic(1.0, 1.0)), [geographic(-1.0, 0.5), geographic(2.0, 0.5)]).
   | ?- geospatial::bbox_union(bbox(geographic(0.0, 0.0), geographic(1.0, 1.0)), bbox(geographic(-1.0, 0.5), geographic(0.5, 2.0)), BoundingBox).
   | ?- geospatial::bbox_expand(bbox(geographic(0.0, 0.0), geographic(0.0, 0.0)), 111.195, ExpandedBoundingBox).

Compute nearest points and distances to paths:

::

   | ?- geospatial::nearest_point_on_segment(geographic(1.0, 1.0), geographic(0.0, 0.0), geographic(0.0, 2.0), Nearest).
   | ?- geospatial::nearest_point_on_polyline(geographic(1.0, 1.0), [geographic(0.0, 0.0), geographic(0.0, 2.0), geographic(2.0, 2.0)], Nearest, Distance).
   | ?- geospatial::point_to_polyline_distance(geographic(1.0, 1.0), [geographic(0.0, 0.0), geographic(0.0, 2.0)], Distance).

Compute polyline and polygon lengths:

::

   | ?- geospatial::polyline_length([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(0.0, 2.0)], Length).
   | ?- geospatial::polyline_length([geographic(0.0, 0.0), geographic(0.0, 1.0)], vincenty, Length).
   | ?- geospatial::polyline_simplify([geographic(0.0, 0.0), geographic(0.5, 0.1), geographic(1.0, 0.0)], 20.0, Simplified).
   | ?- geospatial::polyline_split_at_distance([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(0.0, 2.0)], 111.195, Left, Right).
   | ?- geospatial::polyline_resample([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(0.0, 2.0)], 50.0, Resampled).
   | ?- geospatial::polygon_perimeter([geographic(0.0, 0.0), geographic(0.0, 1.0), geographic(1.0, 1.0), geographic(1.0, 0.0)], Perimeter).

Check polygon intersections:

::

   | ?- geospatial::polygons_intersect([geographic(0.0, 0.0), geographic(0.0, 2.0), geographic(2.0, 2.0), geographic(2.0, 0.0)], [geographic(1.0, 1.0), geographic(1.0, 3.0), geographic(3.0, 3.0), geographic(3.0, 1.0)]).
