.. _filters.hag:

filters.hag
===============================================================================

.. note::

    The HAG filter is deprecated and has been replaced by :ref:`filters.hag_nn`,
    :ref:`filters.hag_dem` and :ref:`filters.hag_delaunay`.  Please use
    the new filter
    that matches your needs as this filter will be removed in a future release.

The **Height Above Ground (HAG) filter** takes as input a point cloud with
``Classification`` set to 2 for ground points (see
`ASPRS Standard LIDAR Point Classes
<http://www.asprs.org/a/society/committees/standards/LAS_1_4_r13.pdf>`_).
It creates a new dimension,
``HeightAboveGround``, that contains the normalized height values.  Ground
points may be generated by :ref:`filters.pmf` or :ref:`filters.smrf`,
but you can use any method you choose, as long as the ground returns are
marked with the classification value of 2.

Normalized heights are a commonly used attribute of point cloud data. This can
also be referred to as *height above ground* (HAG) or *above ground level* (AGL)
heights. In the end, it is simply a measure of a point's relative height as
opposed to its raw elevation value.

There are two modes of operation:

Weighted Nearest Point
----------------------
This default method finds the `count`_ ground points nearest the
non-ground point under consideration.  It calculates an average ground
height weighted by the distance of each ground point from the non-ground
point.  The ``HeightAboveGround`` is the difference between the ``Z`` value
of the non-ground point and the interpolated ground height.

Triangulated Interpolation
--------------------------
Used when the `delaunay`_ option is set to true.  It requires a `count`_
of at least 3.

The filter creates a delaunay triangulation of the `count`_ ground points
closest to the non-ground point in question.  If the non-ground point is
within the trianulated area, the assigned ``HeightAboveGround`` is the
difference between its ``Z`` value and a ground height interpolated from
the three vertices of the containing triangle.  If the non-ground point
is outside of the triangulated area, its ``HeightAboveGround`` is calculated
as the difference between its ``Z`` value and the ``Z`` value of the
nearest ground point.

Choosing a value for `count`_ is difficult, as placing the non-ground
point in the triangulated area depends on the layout of the nearby points.
If, for example, all the ground points near a non-ground point lay on
one side of that non-ground point, finding a containing triangle will fail.

.. embed::

Example #1
----------

Using the autzen dataset (here shown colored by elevation)

.. image:: ./images/autzen-elevation.png
   :height: 400px

we execute the following pipeline

.. code-block:: json

  [
      "autzen.laz",
      {
          "type":"filters.hag"
      },
      {
          "type":"writers.bpf",
          "filename":"autzen-height.bpf",
          "output_dims":"X,Y,Z,HeightAboveGround"
      }
  ]

which is equivalent to the ``pdal translate`` command

::

    $ pdal translate autzen.laz autzen-height.bpf hag \
        --writers.bpf.output_dims="X,Y,Z,HeightAboveGround"

In either case, the result, when colored by the normalized height instead of
elevation is

.. image:: ./images/autzen-height.png
   :height: 400px

Example #2
-------------------------------------------------------------------------------

In the previous example, we chose a :ref:`writer <writers.bpf>` that could
output custom dimensions. If you'd instead like to overwrite your Z values, then
follow the height filter with :ref:`filters.ferry` as shown

.. code-block:: json

  [
      "autzen.laz",
      {
          "type":"filters.hag",
          "count": 10,
          "delaunay": true
      },
      {
          "type":"filters.ferry",
          "dimensions":"HeightAboveGround=>Z"
      },
      "autzen-height-as-Z.laz"
  ]


Example #3
-------------------------------------------------------------------------------

If you don't yet have points classified as ground, start with :ref:`filters.pmf`
or :ref:`filters.smrf` to label ground returns, as shown

.. code-block:: json

  [
      "autzen.laz",
      {
          "type":"filters.smrf"
      },
      {
          "type":"filters.hag",
          "count":4
      },
      {
          "type":"filters.ferry",
          "dimensions":"HeightAboveGround=>Z"
      },
      "autzen-height-as-Z-smrf.laz"
  ]

which is equivalent to the command

::

    $ pdal translate autzen.laz autzen-height-as-Z-smrf.bpf smrf hag ferry \
        --filters.ferry.dimensions="HeightAboveGround=Z" \
        --filters.hag.count=4

Options
-------------------------------------------------------------------------------

_`count`
    The number of ground neighbors to consider when determining the height
    above ground for a non-ground point.  Must be at least 3 when using
    the `delaunay`_ option. [Default: 1]

_`delaunay`
    If true, create a delaunay triangulation of neighboring ground points
    to aid in interpolation of a ground height to use as a difference to
    a point's non-ground height. [Default: false]

max_distance
    Use only ground points within `max_distance` of non-ground point when
    performing neighbor interpolation.  Not used with the `delaunay`_
    option.  [Default: None]

allow_extrapolation
    If false and a non-ground point lies outside of the bounding box of
    all ground points, its ``HeightAboveGround`` is set to 0.  If true
    and ``delaunay`` is set, the ``HeightAboveGround`` is set to the
    difference between the heights of the non-ground point and nearest
    ground point.  If true and using neighbor interpolation (``delaunay``
    is false), extrapolation is used to assign the ``HeightAboveGround``
    value.
    [Default: false]
