Skip to content

Commit

Permalink
Add ST_IsValidTrajectory (#3129)
Browse files Browse the repository at this point in the history
Adds lwgeom_is_trajectory and lwline_is_trajectory to liblwgeom.
Includes unit and regress test.

Includes documentation, starting a new "Temporal support" section
in which ST_ClosestPointOfApproach is also moved

git-svn-id: http://svn.osgeo.org/postgis/trunk@13570 b70326c6-7e19-0410-871a-916f4a2858ee
  • Loading branch information
Sandro Santilli committed May 28, 2015
1 parent 3947f98 commit 03c15d5
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 79 deletions.
1 change: 1 addition & 0 deletions doc/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ XML_SOURCES = \
reference_output.xml \
reference_processing.xml \
reference_raster.xml \
reference_temporal.xml \
reference_transaction.xml \
reference_type.xml \
reference.xml \
Expand Down
1 change: 1 addition & 0 deletions doc/postgis.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<!ENTITY reference_sfcgal SYSTEM "reference_sfcgal.xml">
<!ENTITY reference_processing SYSTEM "reference_processing.xml">
<!ENTITY reference_lrs SYSTEM "reference_lrs.xml">
<!ENTITY reference_temporal SYSTEM "reference_temporal.xml">
<!ENTITY reference_transaction SYSTEM "reference_transaction.xml">
<!ENTITY reference_misc SYSTEM "reference_misc.xml">
<!ENTITY reference_exception SYSTEM "reference_exception.xml">
Expand Down
1 change: 1 addition & 0 deletions doc/reference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
&reference_sfcgal;
&reference_processing;
&reference_lrs;
&reference_temporal;
&reference_transaction;
&reference_misc;
&reference_exception;
Expand Down
79 changes: 0 additions & 79 deletions doc/reference_lrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -598,83 +598,4 @@ ST_GeomFromEWKT('MULTILINESTRINGM((1 0 4, 2 0 4, 4 0 4),(1 0 4, 2 0 4, 4 0 4))')

</refentry>

<refentry id="ST_ClosestPointOfApproach">

<refnamediv>
<refname>ST_ClosestPointOfApproach</refname>
<refpurpose>
Returns the measure at which points interpolated along two lines are closest.
</refpurpose>
</refnamediv>

<refsynopsisdiv>
<funcsynopsis>
<funcprototype>
<funcdef>float8 <function>ST_ClosestPointOfApproach</function></funcdef>
<paramdef><type>geometry </type> <parameter>track1</parameter></paramdef>
<paramdef><type>geometry </type> <parameter>track2</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>

<refsection>
<title>Description</title>

<para>
Returns the smallest measure at which point interpolated along the given
lines are at the smallest distance. Inputs must be LINESTRINGM geometries
and the M value in their vertices are expected to be growing from first
to last vertex.
</para>

<para>
See <xref linkend="ST_LocateAlong" /> for getting the actual points at
the given measure.
</para>

<para>Availability: 2.2.0</para>
<para>&Z_support;</para>
</refsection>


<refsection>
<title>Examples</title>
<programlisting>
-- Return the time in which two objects moving between 10:00 and 11:00
-- are closest to each other and their distance at that point
WITH inp AS ( SELECT
ST_AddMeasure('LINESTRING Z (0 0 0, 10 0 5)'::geometry,
extract(epoch from '2015-05-26 10:00'::timestamptz),
extract(epoch from '2015-05-26 11:00'::timestamptz)
) a,
ST_AddMeasure('LINESTRING Z (0 2 10, 12 1 2)'::geometry,
extract(epoch from '2015-05-26 10:00'::timestamptz),
extract(epoch from '2015-05-26 11:00'::timestamptz)
) b
), cpa AS (
SELECT ST_ClosestPointOfApproach(a,b) m FROM inp
), points AS (
SELECT ST_Force3DZ(ST_GeometryN(ST_LocateAlong(a,m),1)) pa,
ST_Force3DZ(ST_GeometryN(ST_LocateAlong(b,m),1)) pb
FROM inp, cpa
)
SELECT to_timestamp(m) t,
ST_Distance(pa,pb) distance
FROM points, cpa;

t | distance
-------------------------------+------------------
2015-05-26 10:45:31.034483+02 | 1.96036833151395
</programlisting>
</refsection>

<!-- Optionally add a "See Also" section -->
<refsection>
<title>See Also</title>
<para>
<xref linkend="ST_LocateAlong" />,
<xref linkend="ST_AddMeasure" />
</para>
</refsection>
</refentry>
</sect1>
144 changes: 144 additions & 0 deletions doc/reference_temporal.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<sect1 id="Temporal">
<title>Temporal Support</title>

<refentry id="ST_IsValidTrajectory">

<refnamediv>
<refname>ST_IsValidTrajectory</refname>
<refpurpose>
Returns <varname>true</varname> if the geometry is a valid trajectory.
</refpurpose>
</refnamediv>

<refsynopsisdiv>
<funcsynopsis>
<funcprototype>
<funcdef>boolean <function>ST_IsValidTrajectory</function></funcdef>
<paramdef><type>geometry </type> <parameter>line</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>

<refsection>
<title>Description</title>

<para>
Tell if a geometry encodes a valid trajectory.
Valid trajectories are encoded as LINESTRING with M value growing
from each vertex to the next.
</para>

<para>
Valid trajectories are expected as input to some spatio-temporal queries
like <xref linkend="ST_ClosestPointOfApproach" />
</para>

<para>Availability: 2.2.0</para>
<para>&Z_support;</para>
</refsection>


<refsection>
<title>Examples</title>
<programlisting>
SELECT ST_IsValidTrajectory(ST_MakeLine(
ST_MakePointM(0,0,1),
ST_MakePointM(0,1,2))
);
st_isvalidtrajectory
----------------------
t
</programlisting>
</refsection>

<!-- Optionally add a "See Also" section -->
<refsection>
<title>See Also</title>
<para>
<xref linkend="ST_ClosestPointOfApproach" />
</para>
</refsection>
</refentry>

<refentry id="ST_ClosestPointOfApproach">

<refnamediv>
<refname>ST_ClosestPointOfApproach</refname>
<refpurpose>
Returns the measure at which points interpolated along two lines are closest.
</refpurpose>
</refnamediv>

<refsynopsisdiv>
<funcsynopsis>
<funcprototype>
<funcdef>float8 <function>ST_ClosestPointOfApproach</function></funcdef>
<paramdef><type>geometry </type> <parameter>track1</parameter></paramdef>
<paramdef><type>geometry </type> <parameter>track2</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>

<refsection>
<title>Description</title>

<para>
Returns the smallest measure at which point interpolated along the given
lines are at the smallest distance. Inputs must be valid trajectories as
checked by <xref linkend="ST_IsValidTrajectory" />.
</para>

<para>
See <xref linkend="ST_LocateAlong" /> for getting the actual points at
the given measure.
</para>

<para>Availability: 2.2.0</para>
<para>&Z_support;</para>
</refsection>


<refsection>
<title>Examples</title>
<programlisting>
-- Return the time in which two objects moving between 10:00 and 11:00
-- are closest to each other and their distance at that point
WITH inp AS ( SELECT
ST_AddMeasure('LINESTRING Z (0 0 0, 10 0 5)'::geometry,
extract(epoch from '2015-05-26 10:00'::timestamptz),
extract(epoch from '2015-05-26 11:00'::timestamptz)
) a,
ST_AddMeasure('LINESTRING Z (0 2 10, 12 1 2)'::geometry,
extract(epoch from '2015-05-26 10:00'::timestamptz),
extract(epoch from '2015-05-26 11:00'::timestamptz)
) b
), cpa AS (
SELECT ST_ClosestPointOfApproach(a,b) m FROM inp
), points AS (
SELECT ST_Force3DZ(ST_GeometryN(ST_LocateAlong(a,m),1)) pa,
ST_Force3DZ(ST_GeometryN(ST_LocateAlong(b,m),1)) pb
FROM inp, cpa
)
SELECT to_timestamp(m) t,
ST_Distance(pa,pb) distance
FROM points, cpa;

t | distance
-------------------------------+------------------
2015-05-26 10:45:31.034483+02 | 1.96036833151395
</programlisting>
</refsection>

<!-- Optionally add a "See Also" section -->
<refsection>
<title>See Also</title>
<para>
<xref linkend="ST_IsValidTrajectory" />,
<xref linkend="ST_LocateAlong" />,
<xref linkend="ST_AddMeasure" />
</para>
</refsection>
</refentry>

</sect1>
48 changes: 48 additions & 0 deletions liblwgeom/cunit/cu_measures.c
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,53 @@ test_lwgeom_tcpa(void)

}

static void
test_lwgeom_is_trajectory(void)
{
LWGEOM *g;
int ret;

g = lwgeom_from_wkt("POINT M(0 0 1)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_FALSE); /* not a linestring */

g = lwgeom_from_wkt("LINESTRING Z(0 0 1, 0 0 1)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_FALSE); /* no measure */

g = lwgeom_from_wkt("LINESTRING M(0 0 1, 0 0 1)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_FALSE); /* same measure in two points */

g = lwgeom_from_wkt("LINESTRING M(0 0 1, 0 0 0)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_FALSE); /* backward measure */

g = lwgeom_from_wkt("LINESTRING M(0 0 1, 1 0 3, 2 2 2)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_FALSE); /* backward measure */

g = lwgeom_from_wkt("LINESTRING M(0 0 1, 0 0 2)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_TRUE); /* ok (still) */

g = lwgeom_from_wkt("LINESTRING M EMPTY", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_TRUE); /* ok (empty) */

g = lwgeom_from_wkt("LINESTRING M (0 0 1)", LW_PARSER_CHECK_NONE);
ret = lwgeom_is_trajectory(g);
lwgeom_free(g);
ASSERT_INT_EQUAL(ret, LW_TRUE); /* ok (corner case) */
}

/*
** Used by test harness to register the tests in this file.
*/
Expand All @@ -1122,4 +1169,5 @@ void measures_suite_setup(void)
PG_ADD_TEST(suite, test_lw_dist2d_pt_ptarrayarc);
PG_ADD_TEST(suite, test_lw_dist2d_ptarray_ptarrayarc);
PG_ADD_TEST(suite, test_lwgeom_tcpa);
PG_ADD_TEST(suite, test_lwgeom_is_trajectory);
}
6 changes: 6 additions & 0 deletions liblwgeom/cunit/cu_tester.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ typedef void (*PG_SuiteSetup)(void);
CU_ASSERT_EQUAL(o,e); \
} while (0);

#define ASSERT_INT_EQUAL(o,e) do { \
if ( o != e ) \
fprintf(stderr, "[%s:%d]\n Expected: %d\n Obtained: %d\n", __FILE__, __LINE__, (e), (o)); \
CU_ASSERT_EQUAL(o,e); \
} while (0);


7 changes: 7 additions & 0 deletions liblwgeom/liblwgeom.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,13 @@ extern double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt);
*/
extern double lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist);

/**
* Return LW_TRUE or LW_FALSE depending on whether or not a geometry is
* a linestring with measure value growing from start to end vertex
*/
extern int lwgeom_is_trajectory(const LWGEOM *geom);
extern int lwline_is_trajectory(const LWLINE *geom);

/*
* Ensure every segment is at most 'dist' long.
* Returned LWGEOM might is unchanged if a POINT.
Expand Down
11 changes: 11 additions & 0 deletions liblwgeom/lwgeom.c
Original file line number Diff line number Diff line change
Expand Up @@ -2035,4 +2035,15 @@ lwgeom_subdivide(const LWGEOM *geom, int maxvertices)
return col;
}

int
lwgeom_is_trajectory(const LWGEOM *geom)
{
int type = geom->type;

if( type != LINETYPE ) {
lwnotice("Geometry is not a LINESTRING");
return LW_FALSE;
}
return lwline_is_trajectory((LWLINE*)geom);
}

28 changes: 28 additions & 0 deletions liblwgeom/lwline.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,34 @@ lwline_is_closed(const LWLINE *line)
return ptarray_is_closed_2d(line->points);
}

int
lwline_is_trajectory(const LWLINE *line)
{
POINT3DM p;
int i, n;
double m = -1 * FLT_MAX;

if ( ! FLAGS_GET_M(line->flags) ) {
lwnotice("Line does not have M dimension");
return LW_FALSE;
}

n = line->points->npoints;
if ( n < 2 ) return LW_TRUE; /* empty or single-point are "good" */

for (i=0; i<n; ++i) {
getPoint3dm_p(line->points, i, &p);
if ( p.m <= m ) {
lwnotice("Measure of vertex %d (%g) not bigger than measure of vertex %d (%g)",
i, p.m, i-1, m);
return LW_FALSE;
}
m = p.m;
}

return LW_TRUE;
}


LWLINE*
lwline_force_dims(const LWLINE *line, int hasz, int hasm)
Expand Down
Loading

0 comments on commit 03c15d5

Please sign in to comment.