/**********************************************************************
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.net
*
* PostGIS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* PostGIS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PostGIS. If not, see .
*
**********************************************************************
*
* Copyright 2015 Daniel Baston
*
**********************************************************************/
#include "liblwgeom.h"
#include "lwgeom_log.h"
struct LISTNODE
{
struct LISTNODE* next;
void* item;
};
typedef struct LISTNODE LISTNODE;
/* The LWPOINTITERATOR consists of two stacks of items to process: a stack
* of geometries, and a stack of POINTARRAYs extracted from those geometries.
* The index "i" refers to the "next" point, which is found at the top of the
* pointarrays stack.
*
* When the pointarrays stack is depleted, we pull a geometry from the geometry
* stack to replenish it.
*/
struct LWPOINTITERATOR
{
LISTNODE* geoms;
LISTNODE* pointarrays;
uint32_t i;
char allow_modification;
};
static LISTNODE*
prepend_node(void* g, LISTNODE* front)
{
LISTNODE* n = lwalloc(sizeof(LISTNODE));
n->item = g;
n->next = front;
return n;
}
static LISTNODE*
pop_node(LISTNODE* i)
{
LISTNODE* next = i->next;
lwfree(i);
return next;
}
static int
add_lwgeom_to_stack(LWPOINTITERATOR* s, LWGEOM* g)
{
if (lwgeom_is_empty(g))
return LW_FAILURE;
s->geoms = prepend_node(g, s->geoms);
return LW_SUCCESS;
}
/** Return a pointer to the first of one or more LISTNODEs holding the POINTARRAYs
* of a geometry. Will not handle GeometryCollections.
*/
static LISTNODE*
extract_pointarrays_from_lwgeom(LWGEOM* g)
{
switch(lwgeom_get_type(g))
{
case POINTTYPE:
return prepend_node(lwgeom_as_lwpoint(g)->point, NULL);
case LINETYPE:
return prepend_node(lwgeom_as_lwline(g)->points, NULL);
case TRIANGLETYPE:
return prepend_node(lwgeom_as_lwtriangle(g)->points, NULL);
case CIRCSTRINGTYPE:
return prepend_node(lwgeom_as_lwcircstring(g)->points, NULL);
case POLYGONTYPE:
{
LISTNODE* n = NULL;
LWPOLY* p = lwgeom_as_lwpoly(g);
int i;
for (i = p->nrings - 1; i >= 0; i--)
n = prepend_node(p->rings[i], n);
return n;
}
default:
lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(g->type));
}
return NULL;
}
/** Remove an LWCOLLECTION from the iterator stack, and add the components of the
* LWCOLLECTIONs to the stack.
*/
static void
unroll_collection(LWPOINTITERATOR* s)
{
int i;
LWCOLLECTION* c;
if (!s->geoms)
{
return;
}
c = (LWCOLLECTION*) s->geoms->item;
s->geoms = pop_node(s->geoms);
for (i = c->ngeoms - 1; i >= 0; i--)
{
LWGEOM* g = lwcollection_getsubgeom(c, i);
add_lwgeom_to_stack(s, g);
}
}
/** Unroll LWCOLLECTIONs from the top of the stack, as necessary, until the element at the
* top of the stack is not a LWCOLLECTION.
*/
static void
unroll_collections(LWPOINTITERATOR* s)
{
while(s->geoms && lwgeom_is_collection(s->geoms->item))
{
unroll_collection(s);
}
}
static int
lwpointiterator_advance(LWPOINTITERATOR* s)
{
s->i += 1;
/* We've reached the end of our current POINTARRAY. Try to see if there
* are any more POINTARRAYS on the stack. */
if (s->pointarrays && s->i >= ((POINTARRAY*) s->pointarrays->item)->npoints)
{
s->pointarrays = pop_node(s->pointarrays);
s->i = 0;
}
/* We don't have a current POINTARRAY. Pull a geometry from the stack, and
* decompose it into its POINTARRARYs. */
if (!s->pointarrays)
{
LWGEOM* g;
unroll_collections(s);
if (!s->geoms)
{
return LW_FAILURE;
}
s->i = 0;
g = s->geoms->item;
s->pointarrays = extract_pointarrays_from_lwgeom(g);
s->geoms = pop_node(s->geoms);
}
if (!s->pointarrays)
{
return LW_FAILURE;
}
return LW_SUCCESS;
}
/* Public API implementation */
int
lwpointiterator_peek(LWPOINTITERATOR* s, POINT4D* p)
{
if (!lwpointiterator_has_next(s))
return LW_FAILURE;
return getPoint4d_p(s->pointarrays->item, s->i, p);
}
int
lwpointiterator_has_next(LWPOINTITERATOR* s)
{
if (s->pointarrays && s->i < ((POINTARRAY*) s->pointarrays->item)->npoints)
return LW_TRUE;
return LW_FALSE;
}
int
lwpointiterator_next(LWPOINTITERATOR* s, POINT4D* p)
{
if (!lwpointiterator_has_next(s))
return LW_FAILURE;
/* If p is NULL, just advance without reading */
if (p && !lwpointiterator_peek(s, p))
return LW_FAILURE;
lwpointiterator_advance(s);
return LW_SUCCESS;
}
int
lwpointiterator_modify_next(LWPOINTITERATOR* s, const POINT4D* p)
{
if (!lwpointiterator_has_next(s))
return LW_FAILURE;
if (!s->allow_modification)
{
lwerror("Cannot write to read-only iterator");
return LW_FAILURE;
}
ptarray_set_point4d(s->pointarrays->item, s->i, p);
lwpointiterator_advance(s);
return LW_SUCCESS;
}
LWPOINTITERATOR*
lwpointiterator_create(const LWGEOM* g)
{
LWPOINTITERATOR* it = lwpointiterator_create_rw((LWGEOM*) g);
it->allow_modification = LW_FALSE;
return it;
}
LWPOINTITERATOR*
lwpointiterator_create_rw(LWGEOM* g)
{
LWPOINTITERATOR* it = lwalloc(sizeof(LWPOINTITERATOR));
it->geoms = NULL;
it->pointarrays = NULL;
it->i = 0;
it->allow_modification = LW_TRUE;
add_lwgeom_to_stack(it, g);
lwpointiterator_advance(it);
return it;
}
void
lwpointiterator_destroy(LWPOINTITERATOR* s)
{
while (s->geoms != NULL)
{
s->geoms = pop_node(s->geoms);
}
while (s->pointarrays != NULL)
{
s->pointarrays = pop_node(s->pointarrays);
}
lwfree(s);
}