Datum BOX2DFLOAT4_construct(PG_FUNCTION_ARGS)
{
	PG_LWGEOM *min = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	PG_LWGEOM *max = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
	BOX2DFLOAT4 *result = palloc(sizeof(BOX2DFLOAT4));
	LWGEOM *minpoint, *maxpoint;
	POINT2D minp, maxp;

	minpoint = lwgeom_deserialize(SERIALIZED_FORM(min));
	maxpoint = lwgeom_deserialize(SERIALIZED_FORM(max));

	if ( TYPE_GETTYPE(minpoint->type) != POINTTYPE ||
	        TYPE_GETTYPE(maxpoint->type) != POINTTYPE )
	{
		elog(ERROR, "BOX2DFLOAT4_construct: args must be points");
		PG_RETURN_NULL();
	}

	errorIfSRIDMismatch(minpoint->SRID, maxpoint->SRID);

	getPoint2d_p(((LWPOINT *)minpoint)->point, 0, &minp);
	getPoint2d_p(((LWPOINT *)maxpoint)->point, 0, &maxp);

	result->xmax = maxp.x;
	result->ymax = maxp.y;

	result->xmin = minp.x;
	result->ymin = minp.y;

	PG_RETURN_POINTER(result);
}
示例#2
0
static size_t
pointArray_svg_rel(POINTARRAY *pa, char *output, int close_ring, int precision)
{
	int i, end;
	char *ptr;
	char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	POINT2D pt, lpt;

	ptr = output;

	if (close_ring) end = pa->npoints;
	else end = pa->npoints - 1;

	/* Starting point */
	getPoint2d_p(pa, 0, &pt);

	if (fabs(pt.x) < OUT_MAX_DOUBLE)
		sprintf(x, "%.*f", precision, pt.x);
	else
		sprintf(x, "%g", pt.x);
	trim_trailing_zeros(x);

	if (fabs(pt.y) < OUT_MAX_DOUBLE)
		sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1 : pt.y);
	else
		sprintf(y, "%g", fabs(pt.y) ? pt.y * -1 : pt.y);
	trim_trailing_zeros(y);

	ptr += sprintf(ptr,"%s %s l", x, y);

	/* All the following ones */
	for (i=1 ; i < end ; i++)
	{
		lpt = pt;

		getPoint2d_p(pa, i, &pt);
		if (fabs(pt.x -lpt.x) < OUT_MAX_DOUBLE)
			sprintf(x, "%.*f", precision, pt.x -lpt.x);
		else
			sprintf(x, "%g", pt.x -lpt.x);
		trim_trailing_zeros(x);

		/* SVG Y axis is reversed, an no need to transform 0 into -0 */
		if (fabs(pt.y -lpt.y) < OUT_MAX_DOUBLE)
			sprintf(y, "%.*f", precision,
			        fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y));
		else
			sprintf(y, "%g",
			        fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y));
		trim_trailing_zeros(y);

		ptr += sprintf(ptr," %s %s", x, y);
	}

	return (ptr-output);
}
示例#3
0
/**
 * pt_in_ring_2d(): crossing number test for a point in a polygon
 *      input:   p = a point,
 *               pa = vertex points of a ring V[n+1] with V[n]=V[0]
 *      returns: 0 = outside, 1 = inside
 *
 *	Our polygons have first and last point the same,
 *
 */
int
pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring)
{
	int cn = 0;    /* the crossing number counter */
	int i;
	POINT2D v1, v2;

	POINT2D first, last;

	getPoint2d_p(ring, 0, &first);
	getPoint2d_p(ring, ring->npoints-1, &last);
	if ( memcmp(&first, &last, sizeof(POINT2D)) )
	{
		lwerror("pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)",
		        first.x, first.y, last.x, last.y);
		return LW_FALSE;

	}

	LWDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y);
	/* printPA(ring); */

	/* loop through all edges of the polygon */
	getPoint2d_p(ring, 0, &v1);
	for (i=0; i<ring->npoints-1; i++)
	{
		double vt;
		getPoint2d_p(ring, i+1, &v2);

		/* edge from vertex i to vertex i+1 */
		if
		(
		    /* an upward crossing */
		    ((v1.y <= p->y) && (v2.y > p->y))
		    /* a downward crossing */
		    || ((v1.y > p->y) && (v2.y <= p->y))
		)
		{

			vt = (double)(p->y - v1.y) / (v2.y - v1.y);

			/* P.x <intersect */
			if (p->x < v1.x + vt * (v2.x - v1.x))
			{
				/* a valid crossing of y=p.y right of p.x */
				++cn;
			}
		}
		v1 = v2;
	}

	LWDEBUGF(3, "pt_in_ring_2d returning %d", cn&1);

	return (cn&1);    /* 0 if even (out), and 1 if odd (in) */
}
示例#4
0
/**

point to point calculation
*/
int
lw_dist2d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS *dl)
{
	POINT2D p1;
	POINT2D p2;

	getPoint2d_p(point1->point, 0, &p1);
	getPoint2d_p(point2->point, 0, &p2);

	return lw_dist2d_pt_pt(&p1, &p2,dl);
}
示例#5
0
static size_t
assvg_point_buf(const LWPOINT *point, char * output, int circle, int precision)
{
	char *ptr=output;
	char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	POINT2D pt;

	getPoint2d_p(point->point, 0, &pt);

	if (fabs(pt.x) < OUT_MAX_DOUBLE)
		sprintf(x, "%.*f", precision, pt.x);
	else
		sprintf(x, "%g", pt.x);
	trim_trailing_zeros(x);

	/* SVG Y axis is reversed, an no need to transform 0 into -0 */
	if (fabs(pt.y) < OUT_MAX_DOUBLE)
		sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1 : pt.y);
	else
		sprintf(y, "%g", fabs(pt.y) ? pt.y * -1 : pt.y);
	trim_trailing_zeros(y);

	if (circle) ptr += sprintf(ptr, "x=\"%s\" y=\"%s\"", x, y);
	else ptr += sprintf(ptr, "cx=\"%s\" cy=\"%s\"", x, y);

	return (ptr-output);
}
/*
 * return 0 iff point outside polygon or on boundary
 * return 1 iff point inside polygon
 */
int point_in_polygon_rtree(RTREE_NODE **root, int ringCount, LWPOINT *point)
{
	int i;
	POINT2D pt;

	POSTGIS_DEBUGF(2, "point_in_polygon called for %p %d %p.", root, ringCount, point);

	getPoint2d_p(point->point, 0, &pt);
	/* assume bbox short-circuit has already been attempted */

	if (point_in_ring_rtree(root[0], &pt) != 1)
	{
		POSTGIS_DEBUG(3, "point_in_polygon_rtree: outside exterior ring.");

		return 0;
	}

	for (i=1; i<ringCount; i++)
	{
		if (point_in_ring_rtree(root[i], &pt) != -1)
		{
			POSTGIS_DEBUGF(3, "point_in_polygon_rtree: within hole %d.", i);

			return 0;
		}
	}
	return 1;
}
示例#7
0
/**
 * Serializes a LWPOINT to a char*.  This is a helper function that partially
 * writes the appropriate draw and fill commands used to generate an SVG image
 * using ImageMagick's "convert" command.

 * @param output a char reference to write the LWPOINT to
 * @param lwp a reference to a LWPOINT
 * @return the numbers of character written to *output
 */
static size_t
drawPoint(char *output, LWPOINT *lwp, LAYERSTYLE *styles)
{
	char x[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
	char y1[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
	char y2[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
	char *ptr = output;
	POINTARRAY *pa = lwp->point;
	POINT2D p;
	getPoint2d_p(pa, 0, &p);

	LWDEBUGF(4, "%s", "drawPoint called");
	LWDEBUGF( 4, "point = %s", lwgeom_to_ewkt((LWGEOM*)lwp,0) );

	sprintf(x, "%f", p.x);
	trim_trailing_zeros(x);
	sprintf(y1, "%f", p.y);
	trim_trailing_zeros(y1);
	sprintf(y2, "%f", p.y + styles->pointSize);
	trim_trailing_zeros(y2);

	ptr += sprintf(ptr, "-fill %s -strokewidth 0 ", styles->pointColor);
	ptr += sprintf(ptr, "-draw \"circle %s,%s %s,%s", x, y1, x, y2);
	ptr += sprintf(ptr, "'\" ");

	return (ptr - output);
}
/*
 * return -1 if point outside polygon
 * return 0 if point on boundary
 * return 1 if point inside polygon
 *
 * Expected **root order is each exterior ring followed by its holes, eg. EIIEIIEI
 */
int point_in_multipolygon_rtree(RTREE_NODE **root, int polyCount, int *ringCounts, LWPOINT *point)
{
	int i, p, r, in_ring;
	POINT2D pt;
	int result = -1;

	POSTGIS_DEBUGF(2, "point_in_multipolygon_rtree called for %p %d %p.", root, polyCount, point);

	getPoint2d_p(point->point, 0, &pt);
	/* assume bbox short-circuit has already been attempted */

        i = 0; /* the current index into the root array */

	/* is the point inside any of the sub-polygons? */
	for ( p = 0; p < polyCount; p++ )
	{
		in_ring = point_in_ring_rtree(root[i], &pt);
		POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: exterior ring (%d), point_in_ring returned %d", p, in_ring);
		if ( in_ring == -1 ) /* outside the exterior ring */
		{
			POSTGIS_DEBUG(3, "point_in_multipolygon_rtree: outside exterior ring.");
		}
         	else if ( in_ring == 0 ) /* on the boundary */
		{
			POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of exterior ring %d", p);
                        return 0;
		} else {
                	result = in_ring;

	                for(r=1; r<ringCounts[p]; r++)
     	                {
                        	in_ring = point_in_ring_rtree(root[i+r], &pt);
		        	POSTGIS_DEBUGF(4, "point_in_multipolygon_rtree: interior ring (%d), point_in_ring returned %d", r, in_ring);
                        	if (in_ring == 1) /* inside a hole => outside the polygon */
                        	{
                                	POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: within hole %d of exterior ring %d", r, p);
                                	result = -1;
                                	break;
                        	}
                        	if (in_ring == 0) /* on the edge of a hole */
                        	{
			        	POSTGIS_DEBUGF(3, "point_in_multipolygon_rtree: on edge of hole %d of exterior ring %d", r, p);
                                	return 0;
		        	}
                	}
                	/* if we have a positive result, we can short-circuit and return it */
                	if ( result != -1)
                	{
                        	return result;
                	}
                }
                /* increment the index by the total number of rings in the sub-poly */
                /* we do this here in case we short-cutted out of the poly before looking at all the rings */ 
                i += ringCounts[p];
	}

	return result; /* -1 = outside, 0 = boundary, 1 = inside */

}
示例#9
0
static void
ptarray_dp_findsplit(POINTARRAY *pts, int p1, int p2, int *split, double *dist)
{
	int k;
	POINT2D pa, pb, pk;
	double tmp;

	LWDEBUG(4, "function called");

	*dist = -1;
	*split = p1;

	if (p1 + 1 < p2)
	{

		getPoint2d_p(pts, p1, &pa);
		getPoint2d_p(pts, p2, &pb);

		LWDEBUGF(4, "P%d(%f,%f) to P%d(%f,%f)",
		         p1, pa.x, pa.y, p2, pb.x, pb.y);

		for (k=p1+1; k<p2; k++)
		{
			getPoint2d_p(pts, k, &pk);

			LWDEBUGF(4, "P%d(%f,%f)", k, pk.x, pk.y);

			/* distance computation */
			tmp = distance2d_pt_seg(&pk, &pa, &pb);

			if (tmp > *dist)
			{
				*dist = tmp;	/* record the maximum */
				*split = k;

				LWDEBUGF(4, "P%d is farthest (%g)", k, *dist);
			}
		}

	} /* length---should be redone if can == 0 */
	else
	{
		LWDEBUG(3, "segment too short, no split/no dist");
	}

}
示例#10
0
/**
* Find the 2d length of the given #POINTARRAY (even if it's 3d)
*/
double
ptarray_length_2d(const POINTARRAY *pts)
{
	double dist = 0.0;
	int i;
	POINT2D frm;
	POINT2D to;

	if ( pts->npoints < 2 ) return 0.0;
	for (i=0; i<pts->npoints-1; i++)
	{
		getPoint2d_p(pts, i, &frm);
		getPoint2d_p(pts, i+1, &to);
		dist += sqrt( ( (frm.x - to.x)*(frm.x - to.x) )  +
		              ((frm.y - to.y)*(frm.y - to.y) ) );
	}
	return dist;
}
示例#11
0
/**

point to line calculation
*/
int
lw_dist2d_point_line(LWPOINT *point, LWLINE *line, DISTPTS *dl)
{
	POINT2D p;
	POINTARRAY *pa = line->points;
	LWDEBUG(2, "lw_dist2d_point_line is called");
	getPoint2d_p(point->point, 0, &p);
	return lw_dist2d_pt_ptarray(&p, pa, dl);
}
/*
 * return -1 iff point outside multipolygon
 * return 0 iff point on multipolygon boundary
 * return 1 iff point inside multipolygon
 */
int point_in_multipolygon(LWMPOLY *mpolygon, LWPOINT *point)
{
	int i, j, result, in_ring;
	POINT2D pt;

	POSTGIS_DEBUG(2, "point_in_polygon called.");

	getPoint2d_p(point->point, 0, &pt);
	/* assume bbox short-circuit has already been attempted */

	result = -1;

	for (j = 0; j < mpolygon->ngeoms; j++ )
	{

		LWPOLY *polygon = mpolygon->geoms[j];

		/* everything is outside of an empty polygon */
		if ( polygon->nrings == 0 ) continue;

		in_ring = point_in_ring(polygon->rings[0], &pt);
		if ( in_ring == -1) /* outside the exterior ring */
		{
			POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
			continue;
		}
		if ( in_ring == 0 )
		{
			return 0;
		}

		result = in_ring;

		for (i=1; i<polygon->nrings; i++)
		{
			in_ring = point_in_ring(polygon->rings[i], &pt);
			if (in_ring == 1) /* inside a hole => outside the polygon */
			{
				POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
				result = -1;
				break;
			}
			if (in_ring == 0) /* on the edge of a hole */
			{
				POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
				return 0;
			}
		}
		if ( result != -1)
		{
			return result;
		}
	}
	return result;
}
示例#13
0
/**
 * Find the area of the outer ring 
 */
double
lwtriangle_area(const LWTRIANGLE *triangle)
{
	double area=0.0;
	int i;
	POINT2D p1;
	POINT2D p2;

	if (! triangle->points->npoints) return area; /* empty triangle */

	for (i=0; i < triangle->points->npoints-1; i++)
	{
		getPoint2d_p(triangle->points, i, &p1);
		getPoint2d_p(triangle->points, i+1, &p2);
		area += ( p1.x * p2.y ) - ( p1.y * p2.x );
	}

	area  /= 2.0;

	return fabs(area);
}
示例#14
0
/**

 * search all the segments of pointarray to see which one is closest to p1
 * Returns minimum distance between point and pointarray
 */
int
lw_dist2d_pt_ptarray(POINT2D *p, POINTARRAY *pa,DISTPTS *dl)
{
	int t;
	POINT2D	start, end;
	int twist = dl->twisted;

	LWDEBUG(2, "lw_dist2d_pt_ptarray is called");

	getPoint2d_p(pa, 0, &start);

	for (t=1; t<pa->npoints; t++)
	{
		dl->twisted=twist;
		getPoint2d_p(pa, t, &end);
		if (!lw_dist2d_pt_seg(p, &start, &end,dl)) return LW_FALSE;

		if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if  the answer is already given*/
		start = end;
	}

	return LW_TRUE;
}
示例#15
0
/**
 * 1. see if pt in outer boundary. if no, then treat the outer ring like a line
 * 2. if in the boundary, test to see if its in a hole.
 *    if so, then return dist to hole, else return 0 (point in polygon)
 */
int
lw_dist2d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS *dl)
{
	POINT2D p;
	int i;

	LWDEBUG(2, "lw_dist2d_point_poly called");

	getPoint2d_p(point->point, 0, &p);

	if (dl->mode == DIST_MAX)
	{
		LWDEBUG(3, "looking for maxdistance");
		return lw_dist2d_pt_ptarray(&p, poly->rings[0], dl);
	}
	/* Return distance to outer ring if not inside it */
	if ( ! pt_in_ring_2d(&p, poly->rings[0]) )
	{
		LWDEBUG(3, "first point not inside outer-ring");
		return lw_dist2d_pt_ptarray(&p, poly->rings[0], dl);
	}

	/*
	 * Inside the outer ring.
	 * Scan though each of the inner rings looking to
	 * see if its inside.  If not, distance==0.
	 * Otherwise, distance = pt to ring distance
	 */
	for (i=1; i<poly->nrings; i++)
	{
		/* Inside a hole. Distance = pt -> ring */
		if ( pt_in_ring_2d(&p, poly->rings[i]) )
		{
			LWDEBUG(3, " inside an hole");
			return lw_dist2d_pt_ptarray(&p, poly->rings[i], dl);
		}
	}

	LWDEBUG(3, " inside the polygon");
	if (dl->mode == DIST_MIN)
	{
		dl->distance=0.0;
		dl->p1.x=p.x;
		dl->p1.y=p.y;
		dl->p2.x=p.x;
		dl->p2.y=p.y;
	}
	return LW_TRUE; /* Is inside the polygon */
}
示例#16
0
Datum LWGEOM_y_point(PG_FUNCTION_ARGS)
{
	PG_LWGEOM *geom;
	LWPOINT *point = NULL;
	POINT2D p;

	geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));

	if ( TYPE_GETTYPE(geom->type) != POINTTYPE )
		lwerror("Argument to Y() must be a point");

	point = lwgeom_getpoint(SERIALIZED_FORM(geom), 0);

	getPoint2d_p(point->point, 0, &p);

	PG_FREE_IF_COPY(geom, 0);

	PG_RETURN_FLOAT8(p.y);
}
示例#17
0
/**

test each segment of l1 against each segment of l2.
*/
int
lw_dist2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2,DISTPTS *dl)
{
	int t,u;
	POINT2D	start, end;
	POINT2D	start2, end2;
	int twist = dl->twisted;

	LWDEBUGF(2, "lw_dist2d_ptarray_ptarray called (points: %d-%d)",l1->npoints, l2->npoints);

	if (dl->mode == DIST_MAX)/*If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have to be between two vertexes*/
	{
		for (t=0; t<l1->npoints; t++) /*for each segment in L1 */
		{
			getPoint2d_p(l1, t, &start);
			for (u=0; u<l2->npoints; u++) /*for each segment in L2 */
			{
				getPoint2d_p(l2, u, &start2);
				lw_dist2d_pt_pt(&start,&start2,dl);
				LWDEBUGF(4, "maxdist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance);
				LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f",
				         t, u, dl->distance, dl->tolerance);
			}
		}
	}
	else
	{
		getPoint2d_p(l1, 0, &start);
		for (t=1; t<l1->npoints; t++) /*for each segment in L1 */
		{
			getPoint2d_p(l1, t, &end);
			getPoint2d_p(l2, 0, &start2);
			for (u=1; u<l2->npoints; u++) /*for each segment in L2 */
			{
				getPoint2d_p(l2, u, &end2);
				dl->twisted=twist;
				lw_dist2d_seg_seg(&start, &end, &start2, &end2,dl);
				LWDEBUGF(4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance);
				LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f",
				         t, u, dl->distance, dl->tolerance);
				if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if  the answer is already given*/
				start2 = end2;
			}
			start = end;
		}
	}
	return LW_TRUE;
}
示例#18
0
/**
 * Writes the coordinates of a POINTARRAY to a char* where ordinates are
 * separated by a comma and coordinates by a space so that the coordinate
 * pairs can be interpreted by ImageMagick's SVG draw command.
 *
 * @param output a reference to write the POINTARRAY to
 * @param pa a reference to a POINTARRAY
 * @return the numbers of character written to *output
 */
static size_t
pointarrayToString(char *output, POINTARRAY *pa)
{
	char x[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
	char y[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
	int i;
	char *ptr = output;

	for ( i=0; i < pa->npoints; i++ )
	{
		POINT2D pt;
		getPoint2d_p(pa, i, &pt);
		sprintf(x, "%f", pt.x);
		trim_trailing_zeros(x);
		sprintf(y, "%f", pt.y);
		trim_trailing_zeros(y);
		if ( i ) ptr += sprintf(ptr, " ");
		ptr += sprintf(ptr, "%s,%s", x, y);
	}

	return (ptr - output);
}
示例#19
0
/**
 * Returns maximum size of rendered pointarray in bytes.
 */
static size_t
pointArray_svg_abs(POINTARRAY *pa, char *output, int close_ring, int precision)
{
	int i, end;
	char *ptr;
	char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	POINT2D pt;

	ptr = output;

	if (close_ring) end = pa->npoints;
	else end = pa->npoints - 1;

	for (i=0 ; i < end ; i++)
	{
		getPoint2d_p(pa, i, &pt);

		if (fabs(pt.x) < OUT_MAX_DOUBLE)
			sprintf(x, "%.*f", precision, pt.x);
		else
			sprintf(x, "%g", pt.x);
		trim_trailing_zeros(x);

		/* SVG Y axis is reversed, an no need to transform 0 into -0 */
		if (fabs(pt.y) < OUT_MAX_DOUBLE)
			sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1:pt.y);
		else
			sprintf(y, "%g", fabs(pt.y) ? pt.y * -1:pt.y);
		trim_trailing_zeros(y);

		if (i == 1) ptr += sprintf(ptr, " L ");
		else if (i) ptr += sprintf(ptr, " ");
		ptr += sprintf(ptr,"%s %s", x, y);
	}

	return (ptr-output);
}
示例#20
0
Datum LWGEOM_x_point(PG_FUNCTION_ARGS)
{
	GSERIALIZED *geom;
	LWGEOM *lwgeom;
	LWPOINT *point = NULL;
	POINT2D p;

	geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));

	if ( gserialized_get_type(geom) != POINTTYPE )
		lwerror("Argument to X() must be a point");

	lwgeom = lwgeom_from_gserialized(geom);
	point = lwgeom_as_lwpoint(lwgeom);
	
	if ( lwgeom_is_empty(lwgeom) )
		PG_RETURN_NULL();

	getPoint2d_p(point->point, 0, &p);

	PG_FREE_IF_COPY(geom, 0);
	PG_RETURN_FLOAT8(p.x);
}
/*
 * return -1 iff point outside polygon
 * return 0 iff point on boundary
 * return 1 iff point inside polygon
 */
int point_in_polygon(LWPOLY *polygon, LWPOINT *point)
{
	int i, result, in_ring;
	POINTARRAY *ring;
	POINT2D pt;

	POSTGIS_DEBUG(2, "point_in_polygon called.");

	getPoint2d_p(point->point, 0, &pt);
	/* assume bbox short-circuit has already been attempted */

	ring = polygon->rings[0];
	in_ring = point_in_ring(polygon->rings[0], &pt);
	if ( in_ring == -1) /* outside the exterior ring */
	{
		POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring.");
		return -1;
	}
	result = in_ring;

	for (i=1; i<polygon->nrings; i++)
	{
		ring = polygon->rings[i];
		in_ring = point_in_ring(polygon->rings[i], &pt);
		if (in_ring == 1) /* inside a hole => outside the polygon */
		{
			POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i);
			return -1;
		}
		if (in_ring == 0) /* on the edge of a hole */
		{
			POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i);
			return 0;
		}
	}
	return result; /* -1 = outside, 0 = boundary, 1 = inside */
}
示例#22
0
/** In X3D3, coordinates are separated by a space separator
 */
static size_t
pointArray_toX3D3(POINTARRAY *pa, char *output, int precision, int opts, int is_closed)
{
	int i;
	char *ptr;
	char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];

	ptr = output;

	if ( ! FLAGS_GET_Z(pa->flags) )
	{
		for (i=0; i<pa->npoints; i++)
		{
			/** Only output the point if it is not the last point of a closed object or it is a non-closed type **/
			if ( !is_closed || i < (pa->npoints - 1) )
			{
				POINT2D pt;
				getPoint2d_p(pa, i, &pt);

				if (fabs(pt.x) < OUT_MAX_DOUBLE)
					sprintf(x, "%.*f", precision, pt.x);
				else
					sprintf(x, "%g", pt.x);
				trim_trailing_zeros(x);

				if (fabs(pt.y) < OUT_MAX_DOUBLE)
					sprintf(y, "%.*f", precision, pt.y);
				else
					sprintf(y, "%g", pt.y);
				trim_trailing_zeros(y);

				if ( i )
					ptr += sprintf(ptr, " ");
				ptr += sprintf(ptr, "%s %s", x, y);
			}
		}
	}
	else
	{
		for (i=0; i<pa->npoints; i++)
		{
			/** Only output the point if it is not the last point of a closed object or it is a non-closed type **/
			if ( !is_closed || i < (pa->npoints - 1) )
			{
				POINT4D pt;
				getPoint4d_p(pa, i, &pt);

				if (fabs(pt.x) < OUT_MAX_DOUBLE)
					sprintf(x, "%.*f", precision, pt.x);
				else
					sprintf(x, "%g", pt.x);
				trim_trailing_zeros(x);

				if (fabs(pt.y) < OUT_MAX_DOUBLE)
					sprintf(y, "%.*f", precision, pt.y);
				else
					sprintf(y, "%g", pt.y);
				trim_trailing_zeros(y);

				if (fabs(pt.z) < OUT_MAX_DOUBLE)
					sprintf(z, "%.*f", precision, pt.z);
				else
					sprintf(z, "%g", pt.z);
				trim_trailing_zeros(z);

				if ( i )
					ptr += sprintf(ptr, " ");

				ptr += sprintf(ptr, "%s %s %s", x, y, z);
			}
		}
	}

	return ptr-output;
}
/*
 * return -1 iff point is outside ring pts
 * return 1 iff point is inside ring pts
 * return 0 iff point is on ring pts
 */
int point_in_ring_rtree(RTREE_NODE *root, POINT2D *point)
{
	int wn = 0;
	int i;
	double side;
	POINT2D seg1;
	POINT2D seg2;
	LWMLINE *lines;

	POSTGIS_DEBUG(2, "point_in_ring called.");

	lines = findLineSegments(root, point->y);
	if (!lines)
		return -1;

	for (i=0; i<lines->ngeoms; i++)
	{
		getPoint2d_p(lines->geoms[i]->points, 0, &seg1);
		getPoint2d_p(lines->geoms[i]->points, 1, &seg2);


		side = determineSide(&seg1, &seg2, point);

		POSTGIS_DEBUGF(3, "segment: (%.8f, %.8f),(%.8f, %.8f)", seg1.x, seg1.y, seg2.x, seg2.y);
		POSTGIS_DEBUGF(3, "side result: %.8f", side);
		POSTGIS_DEBUGF(3, "counterclockwise wrap %d, clockwise wrap %d", FP_CONTAINS_BOTTOM(seg1.y,point->y,seg2.y), FP_CONTAINS_BOTTOM(seg2.y,point->y,seg1.y));

		/* zero length segments are ignored. */
		if (((seg2.x-seg1.x)*(seg2.x-seg1.x)+(seg2.y-seg1.y)*(seg2.y-seg1.y)) < 1e-12*1e-12)
		{
			POSTGIS_DEBUG(3, "segment is zero length... ignoring.");

			continue;
		}

		/* a point on the boundary of a ring is not contained. */
		if (fabs(side) < 1e-12)
		{
			if (isOnSegment(&seg1, &seg2, point) == 1)
			{
				POSTGIS_DEBUGF(3, "point on ring boundary between points %d, %d", i, i+1);

				return 0;
			}
		}

		/*
		 * If the point is to the left of the line, and it's rising,
		 * then the line is to the right of the point and
		 * circling counter-clockwise, so incremement.
		 */
		if (FP_CONTAINS_BOTTOM(seg1.y,point->y,seg2.y) && side>0)
		{
			POSTGIS_DEBUG(3, "incrementing winding number.");

			++wn;
		}
		/*
		 * If the point is to the right of the line, and it's falling,
		 * then the line is to the right of the point and circling
		 * clockwise, so decrement.
		 */
		else if (FP_CONTAINS_BOTTOM(seg2.y,point->y,seg1.y) && side<0)
		{
			POSTGIS_DEBUG(3, "decrementing winding number.");

			--wn;
		}
	}

	POSTGIS_DEBUGF(3, "winding number %d", wn);

	if (wn == 0)
		return -1;
	return 1;
}
LWPOLY *
lwpoly_grid(LWPOLY *poly, gridspec *grid)
{
	LWPOLY *opoly;
	int ri;
	POINTARRAY **newrings = NULL;
	int nrings = 0;
	double minvisiblearea;

	/*
	 * TODO: control this assertion
	 * it is assumed that, since the grid size will be a pixel,
	 * a visible ring should show at least a white pixel inside,
	 * thus, for a square, that would be grid_xsize*grid_ysize
	 */
	minvisiblearea = grid->xsize * grid->ysize;

	nrings = 0;

	POSTGIS_DEBUGF(3, "grid_polygon3d: applying grid to polygon with %d rings",
	         poly->nrings);

	for (ri=0; ri<poly->nrings; ri++)
	{
		POINTARRAY *ring = poly->rings[ri];
		POINTARRAY *newring;

#if POSTGIS_DEBUG_LEVEL >= 4
		POINT2D p1, p2;
		getPoint2d_p(ring, 0, &p1);
		getPoint2d_p(ring, ring->npoints-1, &p2);
		if ( ! SAMEPOINT(&p1, &p2) )
			POSTGIS_DEBUG(4, "Before gridding: first point != last point");
#endif

		newring = ptarray_grid(ring, grid);

		/* Skip ring if not composed by at least 4 pts (3 segments) */
		if ( newring->npoints < 4 )
		{
			pfree(newring);

			POSTGIS_DEBUGF(3, "grid_polygon3d: ring%d skipped ( <4 pts )", ri);

			if ( ri ) continue;
			else break; /* this is the external ring, no need to work on holes */
		}

#if POSTGIS_DEBUG_LEVEL >= 4
		getPoint2d_p(newring, 0, &p1);
		getPoint2d_p(newring, newring->npoints-1, &p2);
		if ( ! SAMEPOINT(&p1, &p2) )
			POSTGIS_DEBUG(4, "After gridding: first point != last point");
#endif

		POSTGIS_DEBUGF(3, "grid_polygon3d: ring%d simplified from %d to %d points", ri,
		         ring->npoints, newring->npoints);

		/*
		 * Add ring to simplified ring array
		 * (TODO: dinamic allocation of pts_per_ring)
		 */
		if ( ! nrings )
		{
			newrings = palloc(sizeof(POINTARRAY *));
		}
		else
		{
			newrings = repalloc(newrings, sizeof(POINTARRAY *)*(nrings+1));
		}
		if ( ! newrings )
		{
			elog(ERROR, "Out of virtual memory");
			return NULL;
		}
		newrings[nrings++] = newring;
	}

	POSTGIS_DEBUGF(3, "grid_polygon3d: simplified polygon with %d rings", nrings);

	if ( ! nrings ) return NULL;

	opoly = lwpoly_construct(poly->srid, NULL, nrings, newrings);
	return opoly;
}
示例#25
0
/*
 * Given a point, returns the location of closest point on pointarray
 * and, optionally, it's actual distance from the point array.
 */
double
ptarray_locate_point(const POINTARRAY *pa, const POINT4D *p4d, double *mindistout, POINT4D *proj4d)
{
	double mindist=-1;
	double tlen, plen;
	int t, seg=-1;
	POINT4D	start4d, end4d, projtmp;
	POINT2D start, end, proj, p;

	/* Initialize our 2D copy of the input parameter */
	p.x = p4d->x;
	p.y = p4d->y;
	
	if ( ! proj4d ) proj4d = &projtmp;

	getPoint2d_p(pa, 0, &start);
	for (t=1; t<pa->npoints; t++)
	{
		double dist;
		getPoint2d_p(pa, t, &end);
		dist = distance2d_pt_seg(&p, &start, &end);

		if (t==1 || dist < mindist )
		{
			mindist = dist;
			seg=t-1;
		}

		if ( mindist == 0 )
		{
			LWDEBUG(3, "Breaking on mindist=0");
			break;
		}

		start = end;
	}

	if ( mindistout ) *mindistout = mindist;

	LWDEBUGF(3, "Closest segment: %d", seg);
	LWDEBUGF(3, "mindist: %g", mindist);

	/*
	 * We need to project the
	 * point on the closest segment.
	 */
	getPoint4d_p(pa, seg, &start4d);
	getPoint4d_p(pa, seg+1, &end4d);
	closest_point_on_segment(p4d, &start4d, &end4d, proj4d);
	
	/* Copy 4D values into 2D holder */
	proj.x = proj4d->x;
	proj.y = proj4d->y;

	LWDEBUGF(3, "Closest segment:%d, npoints:%d", seg, pa->npoints);

	/* For robustness, force 1 when closest point == endpoint */
	if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, &end) )
	{
		return 1.0;
	}

	LWDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y);

	tlen = ptarray_length_2d(pa);

	LWDEBUGF(3, "tlen %g", tlen);

	/* Location of any point on a zero-length line is 0 */
	/* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */
	if ( tlen == 0 ) return 0;

	plen=0;
	getPoint2d_p(pa, 0, &start);
	for (t=0; t<seg; t++, start=end)
	{
		getPoint2d_p(pa, t+1, &end);
		plen += distance2d_pt_pt(&start, &end);

		LWDEBUGF(4, "Segment %d made plen %g", t, plen);
	}

	plen+=distance2d_pt_pt(&proj, &start);

	LWDEBUGF(3, "plen %g, tlen %g", plen, tlen);

	return plen/tlen;
}
示例#26
0
int
ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance)
{
	unsigned int poff = 0;
	unsigned int npoints;
	unsigned int ncap;
	unsigned int ptsize;

	/* Check for pathology */
	if( ! pa1 || ! pa2 ) 
	{
		lwerror("ptarray_append_ptarray: null input");
		return LW_FAILURE;
	}

	npoints = pa2->npoints;
	
	if ( ! npoints ) return LW_SUCCESS; /* nothing more to do */

	if( FLAGS_GET_READONLY(pa1->flags) )
	{
		lwerror("ptarray_append_ptarray: target pointarray is read-only");
		return LW_FAILURE;
	}

	if( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) )
	{
		lwerror("ptarray_append_ptarray: appending mixed dimensionality is not allowed");
		return LW_FAILURE;
	}

	ptsize = ptarray_point_size(pa1);

	/* Check for duplicate end point */
	if ( pa1->npoints )
	{
		POINT2D tmp1, tmp2;
		getPoint2d_p(pa1, pa1->npoints-1, &tmp1);
		getPoint2d_p(pa2, 0, &tmp2);

		/* If the end point and start point are the same, then don't copy start point */
		if (p2d_same(&tmp1, &tmp2)) {
			poff = 1;
			--npoints;
		}
		else if ( gap_tolerance == 0 || ( gap_tolerance > 0 &&
		           distance2d_pt_pt(&tmp1, &tmp2) > gap_tolerance ) ) 
		{
			lwerror("Second line start point too far from first line end point");
			return LW_FAILURE;
		} 
	}

	/* Check if we need extra space */
	ncap = pa1->npoints + npoints;
	if ( pa1->maxpoints < ncap )
	{
		pa1->maxpoints = ncap > pa1->maxpoints*2 ?
		                 ncap : pa1->maxpoints*2;
		pa1->serialized_pointlist = (uint8_t *)lwrealloc(pa1->serialized_pointlist, ptsize * pa1->maxpoints);
	}

	memcpy(getPoint_internal(pa1, pa1->npoints),
	       getPoint_internal(pa2, poff), ptsize * npoints);

	pa1->npoints = ncap;

	return LW_SUCCESS;
}
示例#27
0
static size_t
pointArray_to_geojson(POINTARRAY *pa, char *output, int precision)
{
	int i;
	char *ptr;
	char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];
	char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1];

	ptr = output;

	if (!FLAGS_GET_Z(pa->flags))
	{
		for (i=0; i<pa->npoints; i++)
		{
			POINT2D pt;
			getPoint2d_p(pa, i, &pt);

			if (fabs(pt.x) < OUT_MAX_DOUBLE)
				sprintf(x, "%.*f", precision, pt.x);
			else
				sprintf(x, "%g", pt.x);
			trim_trailing_zeros(x);

			if (fabs(pt.y) < OUT_MAX_DOUBLE)
				sprintf(y, "%.*f", precision, pt.y);
			else
				sprintf(y, "%g", pt.y);
			trim_trailing_zeros(y);

			if ( i ) ptr += sprintf(ptr, ",");
			ptr += sprintf(ptr, "[%s,%s]", x, y);
		}
	}
	else
	{
		for (i=0; i<pa->npoints; i++)
		{
			POINT4D pt;
			getPoint4d_p(pa, i, &pt);

			if (fabs(pt.x) < OUT_MAX_DOUBLE)
				sprintf(x, "%.*f", precision, pt.x);
			else
				sprintf(x, "%g", pt.x);
			trim_trailing_zeros(x);

			if (fabs(pt.y) < OUT_MAX_DOUBLE)
				sprintf(y, "%.*f", precision, pt.y);
			else
				sprintf(y, "%g", pt.y);
			trim_trailing_zeros(y);

			if (fabs(pt.z) < OUT_MAX_DOUBLE)
				sprintf(z, "%.*f", precision, pt.z);
			else
				sprintf(z, "%g", pt.z);
			trim_trailing_zeros(z);

			if ( i ) ptr += sprintf(ptr, ",");
			ptr += sprintf(ptr, "[%s,%s,%s]", x, y, z);
		}
	}

	return (ptr-output);
}
示例#28
0
/*
 * Convenience functions to hide the POINTARRAY
 * TODO: obsolete this
 */
int
lwpoint_getPoint2d_p(const LWPOINT *point, POINT2D *out)
{
	return getPoint2d_p(point->point, 0, out);
}
示例#29
0
static double ptarray_area_spheroid(const POINTARRAY *pa, const SPHEROID *spheroid)
{
	GEOGRAPHIC_POINT a, b;
	POINT2D p;
	int i;
	double area = 0.0;
	GBOX gbox2d;
	int in_south = LW_FALSE;
	double delta_lon_tolerance;
	double latitude_min;

	gbox2d.flags = gflags(0, 0, 0);

	/* Return zero on non-sensical inputs */
	if ( ! pa || pa->npoints < 4 )
		return 0.0;

	/* Get the raw min/max values for the latitudes */
	ptarray_calculate_gbox(pa, &gbox2d);

	if ( signum(gbox2d.ymin) != signum(gbox2d.ymax) )
		lwerror("ptarray_area_spheroid: cannot handle ptarray that crosses equator");

	/* Geodetic bbox < 0.0 implies geometry is entirely in southern hemisphere */
	if ( gbox2d.ymax < 0.0 )
		in_south = LW_TRUE;

	LWDEBUGF(4, "gbox2d.ymax %.12g", gbox2d.ymax);

	/* Tolerance for strip area calculation */
	if ( in_south )
	{
		delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymin) / 8.0) - 2.0) / 10000.0;
		latitude_min = deg2rad(fabs(gbox2d.ymax));
	}
	else
	{
		delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymax) / 8.0) - 2.0) / 10000.0;
		latitude_min = deg2rad(gbox2d.ymin);
	}

	/* Initialize first point */
	getPoint2d_p(pa, 0, &p);
	geographic_point_init(p.x, p.y, &a);

	for ( i = 1; i < pa->npoints; i++ )
	{
		GEOGRAPHIC_POINT a1, b1;
		double strip_area = 0.0;
		double delta_lon = 0.0;
		LWDEBUGF(4, "edge #%d", i);

		getPoint2d_p(pa, i, &p);
		geographic_point_init(p.x, p.y, &b);

		a1 = a;
		b1 = b;

		/* Flip into north if in south */
		if ( in_south )
		{
			a1.lat = -1.0 * a1.lat;
			b1.lat = -1.0 * b1.lat;
		}

		LWDEBUGF(4, "in_south %d", in_south);

		LWDEBUGF(4, "crosses_dateline(a, b) %d", crosses_dateline(&a, &b) );

		if ( crosses_dateline(&a, &b) )
		{
			double shift;

			if ( a1.lon > 0.0 )
				shift = (M_PI - a1.lon) + 0.088; /* About 5deg more */
			else
				shift = (M_PI - b1.lon) + 0.088; /* About 5deg more */

			LWDEBUGF(4, "shift: %.8g", shift);
			LWDEBUGF(4, "before shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon);
			point_shift(&a1, shift);
			point_shift(&b1, shift);
			LWDEBUGF(4, "after shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon);
			
		}


		delta_lon = fabs(b1.lon - a1.lon);

		LWDEBUGF(4, "a1(%.18g %.18g) b1(%.18g %.18g)", a1.lat, a1.lon, b1.lat, b1.lon);
		LWDEBUGF(4, "delta_lon %.18g", delta_lon);
		LWDEBUGF(4, "delta_lon_tolerance %.18g", delta_lon_tolerance);

		if ( delta_lon > 0.0 )
		{
			if ( delta_lon < delta_lon_tolerance )
			{
				strip_area = spheroid_striparea(&a1, &b1, latitude_min, spheroid);
				LWDEBUGF(4, "strip_area %.12g", strip_area);
				area += strip_area;
			}
			else
			{
				GEOGRAPHIC_POINT p, q;
				double step = floor(delta_lon / delta_lon_tolerance);
				double distance = spheroid_distance(&a1, &b1, spheroid);
				double pDistance = 0.0;
				int j = 0;
				LWDEBUGF(4, "step %.18g", step);
				LWDEBUGF(4, "distance %.18g", distance);
				step = distance / step;
				LWDEBUGF(4, "step %.18g", step);
				p = a1;
				while (pDistance < (distance - step * 1.01))
				{
					double azimuth = spheroid_direction(&p, &b1, spheroid);
					j++;
					LWDEBUGF(4, "  iteration %d", j);
					LWDEBUGF(4, "  azimuth %.12g", azimuth);
					pDistance = pDistance + step;
					LWDEBUGF(4, "  pDistance %.12g", pDistance);
					spheroid_project(&p, spheroid, step, azimuth, &q);
					strip_area = spheroid_striparea(&p, &q, latitude_min, spheroid);
					LWDEBUGF(4, "  strip_area %.12g", strip_area);
					area += strip_area;
					LWDEBUGF(4, "  area %.12g", area);
					p.lat = q.lat;
					p.lon = q.lon;
				}
				strip_area = spheroid_striparea(&p, &b1, latitude_min, spheroid);
				area += strip_area;
			}
		}

		/* B gets incremented in the next loop, so we save the value here */
		a = b;
	}
	return fabs(area);
}
示例#30
0
/**
** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior  of 2 linestrings
** @param l1 first line string
** @param l2 second line string
** @return a #CG_LINE_CROSS_TYPE
**   LINE_NO_CROSS = 0
**   LINE_CROSS_LEFT = -1
**   LINE_CROSS_RIGHT = 1
**   LINE_MULTICROSS_END_LEFT = -2
**   LINE_MULTICROSS_END_RIGHT = 2
**   LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3
**   LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
**
*/
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
{
    int i = 0, j = 0, rv = 0;
    POINT2D p1, p2, q1, q2;
    POINTARRAY *pa1 = NULL, *pa2 = NULL;
    int cross_left = 0;
    int cross_right = 0;
    int first_cross = 0;
    int this_cross = 0;

    pa1 = (POINTARRAY*)l1->points;
    pa2 = (POINTARRAY*)l2->points;

    /* One-point lines can't intersect (and shouldn't exist). */
    if ( pa1->npoints < 2 || pa2->npoints < 2 )
        return LINE_NO_CROSS;

    LWDEBUGF(4, "l1 = %s", lwgeom_to_ewkt((LWGEOM*)l1));
    LWDEBUGF(4, "l2 = %s", lwgeom_to_ewkt((LWGEOM*)l2));

    /* Initialize first point of q */
    rv = getPoint2d_p(pa2, 0, &q1);

    for ( i = 1; i < pa2->npoints; i++ )
    {

        /* Update second point of q to next value */
        rv = getPoint2d_p(pa2, i, &q2);

        /* Initialize first point of p */
        rv = getPoint2d_p(pa1, 0, &p1);

        for ( j = 1; j < pa1->npoints; j++ )
        {

            /* Update second point of p to next value */
            rv = getPoint2d_p(pa1, j, &p2);

            this_cross = lw_segment_intersects(&p1, &p2, &q1, &q2);

            LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1.x, p1.y, p2.x, p2.y);

            if ( this_cross == SEG_CROSS_LEFT )
            {
                LWDEBUG(4,"this_cross == SEG_CROSS_LEFT");
                cross_left++;
                if ( ! first_cross )
                    first_cross = SEG_CROSS_LEFT;
            }

            if ( this_cross == SEG_CROSS_RIGHT )
            {
                LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT");
                cross_right++;
                if ( ! first_cross )
                    first_cross = SEG_CROSS_LEFT;
            }

            /*
            ** Crossing at a co-linearity can be turned handled by extending
            ** segment to next vertext and seeing if the end points straddle
            ** the co-linear segment.
            */
            if ( this_cross == SEG_COLINEAR )
            {
                LWDEBUG(4,"this_cross == SEG_COLINEAR");
                /* TODO: Add logic here and in segment_intersects()
                continue;
                */
            }

            LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION");

            /* Turn second point of p into first point */
            p1 = p2;

        }

        /* Turn second point of q into first point */
        q1 = q2;

    }

    LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right);

    if ( !cross_left && !cross_right )
        return LINE_NO_CROSS;

    if ( !cross_left && cross_right == 1 )
        return LINE_CROSS_RIGHT;

    if ( !cross_right && cross_left == 1 )
        return LINE_CROSS_LEFT;

    if ( cross_left - cross_right == 1 )
        return LINE_MULTICROSS_END_LEFT;

    if ( cross_left - cross_right == -1 )
        return LINE_MULTICROSS_END_RIGHT;

    if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
        return LINE_MULTICROSS_END_SAME_FIRST_LEFT;

    if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
        return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;

    return LINE_NO_CROSS;

}