Skip to content

Commit

Permalink
Merge branch 'master' into 4.0.x
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Mar 3, 2021
2 parents 9ec284b + 962cccb commit 05e9394
Show file tree
Hide file tree
Showing 26 changed files with 618 additions and 156 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## [Unreleased]

## [4.1.0] - 2021-03-03
### Added
- Added `UTMPoint` as a better way of handling UTM zones than the EPSG model does it
- Improved conversion chaining for `CompoundPoint`s
### Changed
- moved `verticalOffsetAndSlope` method from `CompoundPoint` to `VerticalPoint`. This is technically a breaking change, but since the code is only 2 days old shouldn't affect anyone.

## [4.0.1] - 2021-03-01
### Fixed
- Documentation issues
Expand Down
2 changes: 1 addition & 1 deletion docs/builtin_units_angles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Units of angle
==============

.. tip::
All angle units have helper methods ``->asDegrees()``, ``->asRadians()``, ``->add()``, ``->substract()``,
All angle units have helper methods ``->asDegrees()``, ``->asRadians()``, ``->add()``, ``->subtract()``,
``->multiply()`` and ``->divide()``

Degree
Expand Down
2 changes: 1 addition & 1 deletion docs/builtin_units_lengths.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Units of length
===============

.. tip::
All length units have helper methods ``->asMetres()``, ``->add()``, ``->substract()``,
All length units have helper methods ``->asMetres()``, ``->add()``, ``->subtract()``,
``->multiply()`` and ``->divide()``

Metre
Expand Down
2 changes: 1 addition & 1 deletion docs/builtin_units_scales.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Units of scale
==============

.. tip::
All scale units have helper methods ``->asUnity()``, ``->add()``, ``->substract()``,
All scale units have helper methods ``->asUnity()``, ``->add()``, ``->subtract()``,
``->multiply()`` and ``->divide()``

Unity
Expand Down
2 changes: 1 addition & 1 deletion docs/builtin_units_times.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Units of time
=============

.. tip::
All time units have helper methods ``->asYears()``, ``->add()``, ``->substract()``,
All time units have helper methods ``->asYears()``, ``->add()``, ``->subtract()``,
``->multiply()`` and ``->divide()``

Year
Expand Down
10 changes: 5 additions & 5 deletions docs/coordinate_conversions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ Coordinate conversions
======================
PHPCoord can convert coordinates representing a point from one *Coordinate Reference System (CRS)* to another one. In
this simplest case this might just be converting units (e.g. feet to metres or vice versa), but in most cases involves
running one or more complex algorithms involving a **lot** of trigonometry on the numbers.
running one or more complex algorithms on the numbers.

Most algorithms are not standalone, but require parameters to be work - for instance many require the origin point
Most such algorithms are not standalone, but require parameters to work - for instance many require an origin point
to be specified and these points differ across mapping systems even when they utilise the same algorithm. Knowing both
the algorithm(s) to be used and the parameters used to tune them is essential for high-accuracy conversions.
the algorithm(s) to be used and the parameters used to tune them is essential to perform high-accuracy conversions.

.. note::
If the Earth were actually the shape of an ellipsoid, algorithms could be devised so that conversions between systems
could be performed with no absolutely no loss of accuracy - systems would in effect be mathematically equivalent.

Unfortunately the Earth isn't an ellipsoid and (pre-GPS) all coordinates were calculated by individual humans
Unfortunately the Earth isn't an ellipsoid and coordinates are determined by individual humans
operating on the Earth's actual, irregular surface using instruments subject to observation error. That means
that conversions between CRSs are not just converting between mathematical ideals but often convert between
*sets of observations*. When this happens it means that the conversions between the CRSs can only ever be
Expand All @@ -28,7 +28,7 @@ PHPCoord offers two models of operation for coordinate conversion:
* The hard way in which you can directly utilise one of the many implemented algorithms along with the parameters of your
choice to keep full control over the conversion
* The easy way in which PHPCoord consults the built-in EPSG dataset for the relevant algorithm(s) to use and best
parameters to use for your chosen conversion and executes them behind the scenes on your behalf. Recommended.
parameters to use for your chosen conversion and executes them behind the scenes on your behalf.

.. toctree::
:maxdepth: 1
Expand Down
47 changes: 43 additions & 4 deletions docs/coordinate_conversions_easy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ coordinates in the target CRS.

Although PHPCoord has knowledge of thousands of different conversions, this does not cover many scenarios. For example
there is no published direct conversion between a British National Grid reference and a UTM Grid reference. In these
scenarios PHPCoord can "chain" conversions it does know about to achieve the desired result. For that scenario, it would
scenarios PHPCoord can "chain" conversions it does know about to achieve the desired result, for example it would
automatically calculate British National Grid -> OSGB36 -> WGS84 -> UTM. This ability to chain means conversion
between almost any two CRSs is possible as long as they have a common linkage.
between almost any two CRSs is possible as long as they have a common link.

.. code-block:: php
Expand All @@ -29,7 +29,7 @@ between almost any two CRSs is possible as long as they have a common linkage.
new Metre(69741),
Projected::fromSRID(Projected::EPSG_OSGB_1936_BRITISH_NATIONAL_GRID)
);
$toCRS = Projected::fromSRID(Projected::EPSG_WGS_84_UTM_GRID_SYSTEM_NORTHERN_HEMISPHERE);
$toCRS = Projected::fromSRID(Projected::EPSG_WGS_84_UTM_ZONE_31N);
$to = $from->convert($toCRS);
.. note::
Expand Down Expand Up @@ -63,6 +63,45 @@ to ``true``.

In practice this should not affect you as a ``VerticalPoint`` will normally be used as part of a ``CompoundPoint``.

Universal Transverse Mercator (UTM)
-----------------------------------
PHPCoord has 3 different ways of handling UTM references (:ref:`see here for details<utm_points>`).

For conversions that *do not* involve a ``UTMPoint``, use the ``->convert()`` method as described above.

For conversions from a ``GeographicPoint`` to a ``UTMPoint``, call the ``->asUTMPoint()`` method.

.. code-block:: php
$from = GeographicPoint::create(
new Degree(43.642567),
new Degree(-79.387139),
null,
Geographic2D::fromSRID(Geographic2D::EPSG_WGS_84)
);
$to = $from->asUTMPoint();
.. note::
You cannot directly convert to a ``UTMPoint`` from a different kind of ``ProjectedPoint`` or a ``GeocentricPoint``,
you must convert to the relevant ``GeographicPoint`` first. This is because the projection parameters are calculated
dynamically at runtime and are not available to take part in chain creation.

For conversions from a ``UTMPoint`` back to the associated ``GeographicPoint``, call the ``->asGeographicPoint()`` method.

.. code-block:: php
$from = new UTMPoint(
new Metre(630084),
new Metre(4833439),
17,
UTMPoint::HEMISPHERE_NORTH,
Geographic2D::fromSRID(Geographic2D::EPSG_WGS_84)
);
$to = $from->asGeographicPoint();
The ``->convert()`` method *is* present on ``UTMPoint``\s and can be used as normal to convert to any desired CRS
(including the base CRS).

.. rubric:: Footnotes

.. [#f1] There are over 36 million possible combinations of source and target CRS. They have not all been tested...
.. [#f1] There are over 36 million possible combinations of source and target CRS. They haven't *all* been tested...
2 changes: 1 addition & 1 deletion docs/coordinate_conversions_hard.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ They are listed in detail on the following pages.

.. tip::
The first parameter of every method is always a ``CoordinateReferenceSystem`` object representing the target
CRS. For guidance on remaining parameters, please see a textbook, explaining those is far outside the
CRS. For guidance on remaining parameters, please see a textbook as explaining them is far outside the
scope of this documentation.

.. toctree::
Expand Down
15 changes: 0 additions & 15 deletions docs/coordinate_conversions_hard_compound.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,3 @@ Geographic2D with Height Offsets
Angle $longitudeOffset,
Length $geoidUndulation
); // returns a new GeographicPoint
Vertical Offset and Slope
-------------------------

.. code-block:: php
$point = CompoundPoint::create(...);
$newPoint = $point->verticalOffset(
Compound $to,
Angle $ordinate1OfEvaluationPoint,
Angle $ordinate2OfEvaluationPoint,
Length $verticalOffset,
Angle $inclinationInLatitude,
Angle $inclinationInLongitude
); // returns a new CompoundPoint
16 changes: 16 additions & 0 deletions docs/coordinate_conversions_hard_vertical.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,19 @@ Vertical Offset
Vertical $to,
Length $verticalOffset
); // returns a new VerticalPoint
Vertical Offset and Slope
-------------------------

.. code-block:: php
$point = VerticalPoint::create(...);
$newPoint = $point->verticalOffsetAndSlope(
Vertical $to,
Angle $ordinate1OfEvaluationPoint,
Angle $ordinate2OfEvaluationPoint,
Length $verticalOffset,
Angle $inclinationInLatitude,
Angle $inclinationInLongitude,
GeographicPoint $horizontalPoint
); // returns a new VerticalPoint
128 changes: 128 additions & 0 deletions docs/creating_points_projected.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,131 @@ Examples:
$isITM = $point instanceof IrishTransverseMercatorPoint; //true
$asString = (string) $point; // '(715830, 734697)'
.. _utm_points:

Universal Transverse Mercator (UTM)
-----------------------------------
Although one of the most widely used applications of the Transverse Mercator projection, UTM is not actually a map
projection. It's a *system* of map projections, and this distinction means that it does not fit neatly into the
standard data model. Mathematically each UTM zone/hemisphere combination is its own unique projection and
therefore to work with the data you also need to know which zone/hemisphere the coordinates are reference to.
Adding a further layer of complication is that although UTM is most commonly used alongside WGS84, it can be used with
any Geographic CRS so that information needs to be known as well.

PHPCoord has 3 ways to handle this issue.

Treat each zone/hemisphere as a fully independent projection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Pros: no confusion about what the coordinates represent
| Cons: when converting to UTM, you need to know in advance which zone/hemisphere the points reside in
For many ``Geographic`` CRSs, there are corresponding dedicated ``Projected`` CRS for each individual UTM zone and
hemisphere. In total there are over 1000 individual such CRSs defined.

Examples:

.. code-block:: php
<?php
use PHPCoord\CoordinateReferenceSystem\Projected;
use PHPCoord\ProjectedPoint;
use PHPCoord\UnitOfMeasure\Length\Metre;
// Piazza San Marco, Venice
$crs = Projected::fromSRID(Projected::EPSG_WGS_84_UTM_ZONE_33N);
$point = ProjectedPoint::createFromEastingNorthing(
new Metre(291789),
new Metre(5034599),
$crs
);
// Piazza San Marco, Venice
$crs = Projected::fromSRID(Projected::EPSG_ETRS89_UTM_ZONE_33N);
$point = ProjectedPoint::createFromEastingNorthing(
new Metre(291789),
new Metre(5034599),
$crs
);
Prefix easting with the zone
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Pros: you only have to know the hemisphere when converting to UTM (latitude ± 0)
| Cons: coordinates are not distances, WGS84 only
Because the previously described system has some practical difficulties in use when working with points that are not
all from within a single zone, this alternate mechanism is sometimes used. It works by (ab)using the easting coordinate
to store the zone number alongside the actual coordinate resulting from the projection.

.. warning::
Normally the coordinates of a map projection represent real distances on the ground. For UTM, these would be
distance in metres from the origin. However when zone numbers are incorporated into the easting in this way, then
that is no longer true - an easting of 32500000 and an easting of 33500000 are **not** 1000000m apart.

The prefix-based mechanism is made available in PHPCoord for interoperability with other systems, but is discouraged
for use.

Example:

.. code-block:: php
<?php
use PHPCoord\CoordinateReferenceSystem\Projected;
use PHPCoord\ProjectedPoint;
use PHPCoord\UnitOfMeasure\Length\Metre;
// Piazza San Marco, Venice
$crs = Projected::fromSRID(Projected::EPSG_WGS_84_UTM_GRID_SYSTEM_NORTHERN_HEMISPHERE);
$point = ProjectedPoint::createFromEastingNorthing(
new Metre(33291789), // UTM is defined as metres, but this coordinate is actually not...
new Metre(5034599),
$crs
);
Treat UTM as special
^^^^^^^^^^^^^^^^^^^^
| Pros: no pre-calculation needed to determine hemisphere or zone number when converting
| Cons: potentially less interoperability with other systems (does not fit into the EPSG data model)
PHPCoord also offers a way to work with UTM where the zone number and hemisphere are treated as first-class aspects of
the data model rather than shoehorned into one of the coordinates or needing to be extrapolated from the name of the CRS.
This is done via ``UTMPoint`` which is a specialised extension of ``ProjectedPoint``.

.. code-block:: php
public function __construct(
Length $easting,
Length $northing,
int $zone,
string $hemisphere, //one of UTMPoint::HEMISPHERE_NORTH or UTMPoint::HEMISPHERE_SOUTH
Geographic $crs,
?DateTimeInterface $epoch = null
): UTMPoint
Example:

.. code-block:: php
<?php
use PHPCoord\CoordinateReferenceSystem\Geographic;
use PHPCoord\UTMPoint;
use PHPCoord\UnitOfMeasure\Length\Metre;
// Piazza San Marco, Venice
$crs = Geographic2D::fromSRID(Geographic2D::EPSG_WGS_84);
$point = new UTMPoint(
new Metre(291789),
new Metre(5034599),
33,
UTMPoint::HEMISPHERE_NORTH,
$crs
);
$easting = $point->getEasting(); // Metre
$northing = $point->getNorthing(); // Metre
$zone = $point->getZone(); // int
$hemisphere = $point->getHemisphere(); // UTMPoint::HEMISPHERE_NORTH|UTMPoint::HEMISPHERE_SOUTH
$epoch = $point->getCoordinateEpoch(); // DateTimeImmutable|null
$baseCRS = $point->getBaseCRS(); // Geographic
$crs = $point->getCRS(); // Projected (synthesised at runtime, not one from the built-in EPSG set)
$asString = (string) $point; // '33N 291789 5034599'
Loading

0 comments on commit 05e9394

Please sign in to comment.