Commit 03c15d56 authored by Sandro Santilli's avatar Sandro Santilli

Add ST_IsValidTrajectory (#3129)

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
parent 3947f98f
......@@ -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 \
......
......@@ -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">
......
......@@ -28,6 +28,7 @@
&reference_sfcgal;
&reference_processing;
&reference_lrs;
&reference_temporal;
&reference_transaction;
&reference_misc;
&reference_exception;
......
......@@ -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>
<?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>
......@@ -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.
*/
......@@ -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);
}
......@@ -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);
......@@ -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.
......
......@@ -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);
}
......@@ -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)
......
......@@ -776,3 +776,19 @@ Datum ST_ClosestPointOfApproach(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(gs1, 1);
PG_RETURN_FLOAT8(m);
}
/*
* Does the object correctly model a trajectory ?
* Must be a LINESTRINGM with growing measures
*/
Datum ST_IsValidTrajectory(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ST_IsValidTrajectory);
Datum ST_IsValidTrajectory(PG_FUNCTION_ARGS)
{
GSERIALIZED *gs0 = PG_GETARG_GSERIALIZED_P(0);
/* All checks already performed by liblwgeom, not worth checking again */
LWGEOM *g0 = lwgeom_from_gserialized(gs0);
int ret = lwgeom_is_trajectory(g0);
lwgeom_free(g0);
PG_RETURN_BOOL(ret == LW_TRUE);
}
......@@ -3053,6 +3053,12 @@ CREATE OR REPLACE FUNCTION ST_ClosestPointOfApproach(geometry, geometry)
RETURNS float8
AS 'MODULE_PATHNAME', 'ST_ClosestPointOfApproach'
LANGUAGE 'c' IMMUTABLE STRICT;
-- Availability: 2.2.0
CREATE OR REPLACE FUNCTION ST_IsValidTrajectory(geometry)
RETURNS bool
AS 'MODULE_PATHNAME', 'ST_IsValidTrajectory'
LANGUAGE 'c' IMMUTABLE STRICT;
---------------------------------------------------------------
-- GEOS
......
......@@ -126,6 +126,7 @@ TESTS = \
wmsservers \
wkt \
wkb \
temporal \
tickets \
typmod \
remove_repeated_points \
......
SELECT 'invalidTrajectory1', ST_IsValidTrajectory('POINTM(0 0 0)'::geometry);
SELECT 'invalidTrajectory2', ST_IsValidTrajectory('LINESTRINGZ(0 0 0,1 1 1)'::geometry);
SELECT 'invalidTrajectory3', ST_IsValidTrajectory('LINESTRINGM(0 0 0,1 1 0)'::geometry);
SELECT 'invalidTrajectory4', ST_IsValidTrajectory('LINESTRINGM(0 0 1,1 1 0)'::geometry);
SELECT 'invalidTrajectory8', ST_IsValidTrajectory('LINESTRINGM(0 0 0,1 1 1,1 1 2,1 0 1)'::geometry);
SELECT 'validTrajectory1', ST_IsValidTrajectory('LINESTRINGM(0 0 0,1 1 1)'::geometry);
SELECT 'validTrajectory2', ST_IsValidTrajectory('LINESTRINGM EMPTY'::geometry);
SELECT 'validTrajectory3', ST_IsValidTrajectory('LINESTRINGM(0 0 0,1 1 1,1 1 2)'::geometry);
NOTICE: Geometry is not a LINESTRING
invalidTrajectory1|f
NOTICE: Line does not have M dimension
invalidTrajectory2|f
NOTICE: Measure of vertex 1 (0) not bigger than measure of vertex 0 (0)
invalidTrajectory3|f
NOTICE: Measure of vertex 1 (0) not bigger than measure of vertex 0 (1)
invalidTrajectory4|f
NOTICE: Measure of vertex 3 (1) not bigger than measure of vertex 2 (2)
invalidTrajectory8|f
validTrajectory1|t
validTrajectory2|t
validTrajectory3|t
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment