📄 mitab_geometry.cpp
字号:
/**********************************************************************
* $Id: mitab_geometry.cpp,v 1.5 2004/06/30 20:29:04 dmorissette Exp $
*
* Name: mitab_geometry.cpp
* Project: MapInfo TAB Read/Write library
* Language: C++
* Purpose: Geometry manipulation functions.
* Author: Daniel Morissette, dmorissette@dmsolutions.ca
* Based on functions from mapprimitive.c/mapsearch.c in the source
* of UMN MapServer by Stephen Lime (http://mapserver.gis.umn.edu/)
**********************************************************************
* Copyright (c) 1999-2001, Daniel Morissette
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**********************************************************************
*
* $Log: mitab_geometry.cpp,v $
* Revision 1.5 2004/06/30 20:29:04 dmorissette
* Fixed refs to old address danmo@videotron.ca
*
* Revision 1.4 2001/12/18 23:42:28 daniel
* Added a test in OGRPolygonLabelPoint() to prevent returning a point
* outside of the polygon MBR (bug 673).
*
* Revision 1.3 2001/01/22 16:03:58 warmerda
* expanded tabs
*
* Revision 1.2 2000/09/28 16:39:44 warmerda
* avoid warnings for unused, and unitialized variables
*
* Revision 1.1 2000/09/19 17:19:40 daniel
* Initial Revision
*
**********************************************************************/
#include "ogr_geometry.h"
#define OGR_NUM_RINGS(poly) (poly->getNumInteriorRings()+1)
#define OGR_GET_RING(poly, i) (i==0?poly->getExteriorRing():poly->getInteriorRing(i-1))
/**********************************************************************
* OGRPointInRing()
*
* Returns TRUE is point is inside ring, FALSE otherwise
*
* Adapted version of msPointInPolygon() from MapServer's mapsearch.c
**********************************************************************/
GBool OGRPointInRing(OGRPoint *poPoint, OGRLineString *poRing)
{
int i, j, numpoints;
GBool status = FALSE;
double x, y;
numpoints = poRing->getNumPoints();
x = poPoint->getX();
y = poPoint->getY();
for (i = 0, j = numpoints-1; i < numpoints; j = i++)
{
if ((((poRing->getY(i)<=y) && (y<poRing->getY(j))) ||
((poRing->getY(j)<=y) && (y<poRing->getY(i)))) &&
(x < (poRing->getX(j) - poRing->getX(i)) * (y - poRing->getY(i)) /
(poRing->getY(j) - poRing->getY(i)) + poRing->getX(i)))
status = !status;
}
return status;
}
/**********************************************************************
* OGRIntersectPointPolygon()
*
* Instead of using ring orientation we count the number of parts the
* point falls in. If odd the point is in the polygon, if 0 or even
* then the point is in a hole or completely outside.
*
* Returns TRUE is point is inside polygon, FALSE otherwise
*
* Adapted version of msIntersectPointPolygon() from MapServer's mapsearch.c
**********************************************************************/
GBool OGRIntersectPointPolygon(OGRPoint *poPoint, OGRPolygon *poPoly)
{
int i;
GBool status = FALSE;
for(i=0; i<OGR_NUM_RINGS(poPoly); i++)
{
if (OGRPointInRing(poPoint, OGR_GET_RING(poPoly, i)))
{
/* ok, the point is in a line */
status = !status;
}
}
return status;
}
/**********************************************************************
* OGRPolygonLabelPoint()
*
* Generate a label point on the surface of a polygon.
*
* The function is based on a scanline conversion routine used for polygon
* fills. Instead of processing each line the as with drawing, the
* polygon is sampled. The center of the longest sample is chosen for the
* label point. The label point is guaranteed to be in the polygon even if
* it has holes assuming the polygon is properly formed.
*
* Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
*
* Adapted version of msPolygonLabelPoint() from MapServer's mapprimitive.c
**********************************************************************/
typedef enum {CLIP_LEFT, CLIP_MIDDLE, CLIP_RIGHT} CLIP_STATE;
#define CLIP_CHECK(min, a, max) ((a) < (min) ? CLIP_LEFT : ((a) > (max) ? CLIP_RIGHT : CLIP_MIDDLE));
#define ROUND(a) ( (a) + 0.5 )
#define SWAP( a, b, t) ( (t) = (a), (a) = (b), (b) = (t) )
#define EDGE_CHECK( x0, x, x1) ((x) < MIN( (x0), (x1)) ? CLIP_LEFT : ((x) > MAX( (x0), (x1)) ? CLIP_RIGHT : CLIP_MIDDLE ))
#define NUM_SCANLINES 5
int OGRPolygonLabelPoint(OGRPolygon *poPoly, OGRPoint *poLabelPoint)
{
double slope;
OGRRawPoint point1, point2;
int i, j, k, nfound;
double x, y, *xintersect, temp;
double hi_y, lo_y;
int wrong_order, n;
double len, max_len=0;
double skip;
OGREnvelope oEnv;
if (poPoly == NULL)
return OGRERR_FAILURE;
poPoly->getEnvelope(&oEnv);
poLabelPoint->setX((oEnv.MaxX + oEnv.MinX)/2.0);
poLabelPoint->setY((oEnv.MaxY + oEnv.MinY)/2.0);
//if(get_centroid(p, lp, &miny, &maxy) == -1) return(-1);
if(OGRIntersectPointPolygon(poLabelPoint, poPoly) == TRUE) /* cool, done */
return OGRERR_NONE;
/* do it the hard way - scanline */
skip = (oEnv.MaxY - oEnv.MinY)/NUM_SCANLINES;
n=0;
for(j=0; j<OGR_NUM_RINGS(poPoly); j++)
{
/* count total number of points */
n += OGR_GET_RING(poPoly, j)->getNumPoints();
}
xintersect = (double *)calloc(n, sizeof(double));
if (xintersect == NULL)
return OGRERR_FAILURE;
for(k=1; k<=NUM_SCANLINES; k++)
{
/* sample the shape in the y direction */
y = oEnv.MaxY - k*skip;
/* need to find a y that won't intersect any vertices exactly */
hi_y = y - 1; /* first initializing lo_y, hi_y to be any 2 pnts on either side of lp->y */
lo_y = y + 1;
for(j=0; j<OGR_NUM_RINGS(poPoly); j++)
{
OGRLinearRing *poRing = OGR_GET_RING(poPoly,j);
if((lo_y < y) && (hi_y >= y))
break; /* already initialized */
for(i=0; i < poRing->getNumPoints(); i++)
{
if((lo_y < y) && (hi_y >= y))
break; /* already initialized */
if(poRing->getY(i) < y)
lo_y = poRing->getY(i);
if(poRing->getY(i) >= y)
hi_y = poRing->getY(i);
}
}
n=0;
for(j=0; j<OGR_NUM_RINGS(poPoly); j++)
{
OGRLinearRing *poRing = OGR_GET_RING(poPoly,j);
for(i=0; i < poRing->getNumPoints(); i++)
{
if((poRing->getY(i) < y) &&
((y - poRing->getY(i)) < (y - lo_y)))
lo_y = poRing->getY(i);
if((poRing->getY(i) >= y) &&
((poRing->getY(i) - y) < (hi_y - y)))
hi_y = poRing->getY(i);
}
}
if(lo_y == hi_y)
return OGRERR_FAILURE;
else
y = (hi_y + lo_y)/2.0;
nfound = 0;
for(j=0; j<OGR_NUM_RINGS(poPoly); j++) /* for each line */
{
OGRLinearRing *poRing = OGR_GET_RING(poPoly,j);
point1.x = poRing->getX(poRing->getNumPoints()-1);
point1.y = poRing->getY(poRing->getNumPoints()-1);
for(i=0; i < poRing->getNumPoints(); i++)
{
point2.x = poRing->getX(i);
point2.y = poRing->getY(i);
if(EDGE_CHECK(point1.y, y, point2.y) == CLIP_MIDDLE)
{
if(point1.y == point2.y)
continue; /* ignore horizontal edges */
else
slope = (point2.x - point1.x) / (point2.y - point1.y);
x = point1.x + (y - point1.y)*slope;
xintersect[nfound++] = x;
} /* End of checking this edge */
point1 = point2; /* Go on to next edge */
}
} /* Finished the scanline */
/* First, sort the intersections */
do
{
wrong_order = 0;
for(i=0; i < nfound-1; i++)
{
if(xintersect[i] > xintersect[i+1])
{
wrong_order = 1;
SWAP(xintersect[i], xintersect[i+1], temp);
}
}
} while(wrong_order);
/* Great, now find longest span */
point1.y = point2.y = y;
for(i=0; i < nfound; i += 2)
{
point1.x = xintersect[i];
point2.x = xintersect[i+1];
/* len = length(point1, point2); */
len = ABS((point2.x - point1.x));
if(len > max_len)
{
max_len = len;
poLabelPoint->setX( (point1.x + point2.x)/2 );
poLabelPoint->setY( y );
}
}
}
free(xintersect);
/* __TODO__ Bug 673
* There seem to be some polygons for which the label is returned
* completely outside of the polygon's MBR and this messes the
* file bounds, etc.
* Until we find the source of the problem, we'll at least validate
* the label point to make sure that it overlaps the polygon MBR.
*/
if( poLabelPoint->getX() < oEnv.MinX
|| poLabelPoint->getY() < oEnv.MinY
|| poLabelPoint->getX() > oEnv.MaxX
|| poLabelPoint->getY() > oEnv.MaxY )
{
// Reset label coordinates to center of MBR, just in case
poLabelPoint->setX((oEnv.MaxX + oEnv.MinX)/2.0);
poLabelPoint->setY((oEnv.MaxY + oEnv.MinY)/2.0);
// And return an error
return OGRERR_FAILURE;
}
if(max_len > 0)
return OGRERR_NONE;
else
return OGRERR_FAILURE;
}
/**********************************************************************
* OGRGetCentroid()
*
* Calculate polygon gravity center.
*
* Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
*
* Adapted version of get_centroid() from MapServer's mapprimitive.c
**********************************************************************/
int OGRGetCentroid(OGRPolygon *poPoly, OGRPoint *poCentroid)
{
int i,j;
double cent_weight_x=0.0, cent_weight_y=0.0;
double len, total_len=0;
for(i=0; i<OGR_NUM_RINGS(poPoly); i++)
{
double x1, y1, x2, y2;
OGRLinearRing *poRing = OGR_GET_RING(poPoly, i);
x2 = poRing->getX(0);
y2 = poRing->getY(0);
for(j=1; j<poRing->getNumPoints(); j++)
{
x1 = x2;
y1 = y2;
x2 = poRing->getX(j);
y2 = poRing->getY(j);
len = sqrt( pow((x2-x1),2) + pow((y2-y1),2) );
cent_weight_x += len * ((x1 + x2)/2.0);
cent_weight_y += len * ((y1 + y2)/2.0);
total_len += len;
}
}
if(total_len == 0)
return(OGRERR_FAILURE);
poCentroid->setX( cent_weight_x / total_len );
poCentroid->setY( cent_weight_y / total_len );
return OGRERR_NONE;
}
/**********************************************************************
* OGRPolylineCenterPoint()
*
* Return the center point of a polyline.
*
* In MapInfo, for a simple or multiple polyline (pline), the center point
* in the object definition is supposed to be either the center point of
* the pline or the first section of a multiple pline (if an odd number of
* points in the pline or first section), or the midway point between the
* two central points (if an even number of points involved).
*
* Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
**********************************************************************/
int OGRPolylineCenterPoint(OGRLineString *poLine, OGRPoint *poLabelPoint)
{
if (poLine == NULL || poLine->getNumPoints() < 2)
return OGRERR_FAILURE;
if (poLine->getNumPoints() % 2 == 0)
{
// Return the midway between the 2 center points
int i = poLine->getNumPoints()/2;
poLabelPoint->setX( (poLine->getX(i-1) + poLine->getX(i))/2.0 );
poLabelPoint->setY( (poLine->getY(i-1) + poLine->getY(i))/2.0 );
}
else
{
// Return the center point
poLine->getPoint(poLine->getNumPoints()/2, poLabelPoint);
}
return OGRERR_NONE;
}
/**********************************************************************
* OGRPolylineLabelPoint()
*
* Generate a label point on a polyline: The center of the longest segment.
*
* Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise.
**********************************************************************/
int OGRPolylineLabelPoint(OGRLineString *poLine, OGRPoint *poLabelPoint)
{
double segment_length, max_segment_length = 0.0;
double x1, y1, x2, y2;
if (poLine == NULL || poLine->getNumPoints() < 2)
return OGRERR_FAILURE;
max_segment_length = -1.0;
x2 = poLine->getX(0);
y2 = poLine->getY(0);
for(int i=1; i<poLine->getNumPoints(); i++)
{
x1 = x2;
y1 = y2;
x2 = poLine->getX(i);
y2 = poLine->getY(i);
segment_length = pow((x2-x1),2) + pow((y2-y1),2);
if (segment_length > max_segment_length)
{
max_segment_length = segment_length;
poLabelPoint->setX( (x1 + x2)/2.0 );
poLabelPoint->setY( (y1 + y2)/2.0 );
}
}
return OGRERR_NONE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -