1 /******************************************************************************
2  *
3  * Purpose:  Oracle curve to linestring stroking (approximation).
4  * Author:   Frank Warmerdam, warmerdam@pobox.com
5  *
6  ******************************************************************************
7  * Copyright (c) 2008, Frank Warmerdam
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  ****************************************************************************/
27 
28 #include <stdarg.h>
29 #include "ogr_oci.h"
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32 
33 CPL_CVSID("$Id: ntfstroke.cpp 10645 2007-01-18 02:22:39Z warmerdam $");
34 
35 #ifndef PI
36 #define PI  3.14159265358979323846
37 #endif
38 
39 /************************************************************************/
40 /*                   OGROCIArcCenterFromEdgePoints()                    */
41 /*                                                                      */
42 /*      Compute the center of an arc/circle from three edge points.     */
43 /************************************************************************/
44 
45 static int
OGROCIArcCenterFromEdgePoints(double x_c0,double y_c0,double x_c1,double y_c1,double x_c2,double y_c2,double * x_center,double * y_center)46 OGROCIArcCenterFromEdgePoints( double x_c0, double y_c0,
47                                double x_c1, double y_c1,
48                                double x_c2, double y_c2,
49                                double *x_center, double *y_center )
50 
51 {
52 
53 /* -------------------------------------------------------------------- */
54 /*      Handle a degenerate case that occurs in OSNI products by        */
55 /*      making some assumptions.  If the first and third points are     */
56 /*      the same assume they are intended to define a full circle,      */
57 /*      and that the second point is on the opposite side of the        */
58 /*      circle.                                                         */
59 /* -------------------------------------------------------------------- */
60     if( x_c0 == x_c2 && y_c0 == y_c2 )
61     {
62         *x_center = (x_c0 + x_c1) * 0.5;
63         *y_center = (y_c0 + y_c1) * 0.5;
64 
65         return TRUE;
66     }
67 
68 /* -------------------------------------------------------------------- */
69 /*      Compute the inverse of the slopes connecting the first and      */
70 /*      second points.  Also compute the center point of the two        */
71 /*      lines ... the point our crossing line will go through.          */
72 /* -------------------------------------------------------------------- */
73     double m1, x1, y1;
74 
75     if( (y_c1 - y_c0) != 0.0 )
76         m1 = (x_c0 - x_c1) / (y_c1 - y_c0);
77     else
78         m1 = 1e+10;
79 
80     x1 = (x_c0 + x_c1) * 0.5;
81     y1 = (y_c0 + y_c1) * 0.5;
82 
83 /* -------------------------------------------------------------------- */
84 /*      Compute the same for the second point compared to the third     */
85 /*      point.                                                          */
86 /* -------------------------------------------------------------------- */
87     double m2, x2, y2;
88 
89     if( (y_c2 - y_c1) != 0.0 )
90         m2 = (x_c1 - x_c2) / (y_c2 - y_c1);
91     else
92         m2 = 1e+10;
93 
94     x2 = (x_c1 + x_c2) * 0.5;
95     y2 = (y_c1 + y_c2) * 0.5;
96 
97 /* -------------------------------------------------------------------- */
98 /*      Turn these into the Ax+By+C = 0 form of the lines.              */
99 /* -------------------------------------------------------------------- */
100     double      a1, a2, b1, b2, c1, c2;
101 
102     a1 = m1;
103     a2 = m2;
104 
105     b1 = -1.0;
106     b2 = -1.0;
107 
108     c1 = (y1 - m1*x1);
109     c2 = (y2 - m2*x2);
110 
111 /* -------------------------------------------------------------------- */
112 /*      Compute the intersection of the two lines through the center    */
113 /*      of the circle, using Kramers rule.                              */
114 /* -------------------------------------------------------------------- */
115     double      det_inv;
116 
117     if( a1*b2 - a2*b1 == 0.0 )
118         return FALSE;
119 
120     det_inv = 1 / (a1*b2 - a2*b1);
121 
122     *x_center = (b1*c2 - b2*c1) * det_inv;
123     *y_center = (a2*c1 - a1*c2) * det_inv;
124 
125     return TRUE;
126 }
127 
128 /************************************************************************/
129 /*                OGROCIStrokeArcToOGRGeometry_Angles()                 */
130 /************************************************************************/
131 
132 static int
OGROCIStrokeArcToOGRGeometry_Angles(double dfCenterX,double dfCenterY,double dfRadius,double dfStartAngle,double dfEndAngle,double dfMaxAngleStepSizeDegrees,OGRLineString * poLine)133 OGROCIStrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY,
134                                      double dfRadius,
135                                      double dfStartAngle, double dfEndAngle,
136                                      double dfMaxAngleStepSizeDegrees,
137                                      OGRLineString *poLine )
138 
139 {
140     double             dfArcX, dfArcY, dfSlice;
141     int                iPoint, iAppendLocation, nVertexCount;
142     double             dfEps = dfRadius / 100000.0;
143 
144     nVertexCount = (int)
145         ceil(fabs(dfEndAngle - dfStartAngle)/dfMaxAngleStepSizeDegrees) + 1;
146     nVertexCount = MAX(2,nVertexCount);
147     dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1);
148 
149     for( iPoint=0; iPoint < nVertexCount; iPoint++ )
150     {
151         double      dfAngle;
152 
153         dfAngle = (dfStartAngle + iPoint * dfSlice) * PI / 180.0;
154 
155         dfArcX = dfCenterX + cos(dfAngle) * dfRadius;
156         dfArcY = dfCenterY + sin(dfAngle) * dfRadius;
157 
158         if( iPoint == 0 )
159         {
160             iAppendLocation = poLine->getNumPoints();
161 
162             if( poLine->getNumPoints() > 0
163                 && fabs(poLine->getX(poLine->getNumPoints()-1)-dfArcX) < dfEps
164                 && fabs(poLine->getY(poLine->getNumPoints()-1)-dfArcY) < dfEps)
165             {
166                 poLine->setNumPoints(
167                     poLine->getNumPoints() + nVertexCount - 1 );
168             }
169             else
170             {
171                 poLine->setNumPoints(
172                     poLine->getNumPoints() + nVertexCount - 1 );
173                 poLine->setPoint( iAppendLocation++, dfArcX, dfArcY );
174             }
175         }
176         else
177             poLine->setPoint( iAppendLocation++, dfArcX, dfArcY );
178     }
179 
180     return TRUE;
181 }
182 
183 
184 /************************************************************************/
185 /*                OGROCIStrokeArcToOGRGeometry_Points()                 */
186 /************************************************************************/
187 
188 int
OGROCIStrokeArcToOGRGeometry_Points(double dfStartX,double dfStartY,double dfAlongX,double dfAlongY,double dfEndX,double dfEndY,double dfMaxAngleStepSizeDegrees,int bForceWholeCircle,OGRLineString * poLine)189 OGROCIStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY,
190                                      double dfAlongX, double dfAlongY,
191                                      double dfEndX, double dfEndY,
192                                      double dfMaxAngleStepSizeDegrees,
193                                      int bForceWholeCircle,
194                                      OGRLineString *poLine )
195 
196 {
197     double      dfStartAngle, dfEndAngle, dfAlongAngle;
198     double      dfCenterX, dfCenterY, dfRadius;
199 
200     if( !OGROCIArcCenterFromEdgePoints( dfStartX, dfStartY,
201                                         dfAlongX, dfAlongY,
202                                         dfEndX, dfEndY,
203                                         &dfCenterX, &dfCenterY ) )
204         return FALSE;
205 
206     if( bForceWholeCircle || (dfStartX == dfEndX && dfStartY == dfEndY) )
207     {
208         dfStartAngle = 0.0;
209         dfEndAngle = 360.0;
210     }
211     else
212     {
213         double  dfDeltaX, dfDeltaY;
214 
215         dfDeltaX = dfStartX - dfCenterX;
216         dfDeltaY = dfStartY - dfCenterY;
217         dfStartAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI;
218 
219         dfDeltaX = dfAlongX - dfCenterX;
220         dfDeltaY = dfAlongY - dfCenterY;
221         dfAlongAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI;
222 
223         dfDeltaX = dfEndX - dfCenterX;
224         dfDeltaY = dfEndY - dfCenterY;
225         dfEndAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI;
226 
227         // Try positive (clockwise?) winding.
228         while( dfAlongAngle < dfStartAngle )
229             dfAlongAngle += 360.0;
230 
231         while( dfEndAngle < dfAlongAngle )
232             dfEndAngle += 360.0;
233 
234         // If that doesn't work out, then go anticlockwise.
235         if( dfEndAngle - dfStartAngle > 360.0 )
236         {
237             while( dfAlongAngle > dfStartAngle )
238                 dfAlongAngle -= 360.0;
239 
240             while( dfEndAngle > dfAlongAngle )
241                 dfEndAngle -= 360.0;
242         }
243     }
244 
245     dfRadius = sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX)
246                      + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) );
247 
248     int bResult;
249 
250     bResult =
251         OGROCIStrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY,
252                                              dfRadius,
253                                              dfStartAngle, dfEndAngle,
254                                              dfMaxAngleStepSizeDegrees,
255                                              poLine );
256 
257 /* -------------------------------------------------------------------- */
258 /*      Force the points for arcs, to avoid odd rounding/math           */
259 /*      issues.  Perhaps we should do this for the start too, but       */
260 /*      this is a bit tricky since it isn't obvious which point is      */
261 /*      the start.                                                      */
262 /* -------------------------------------------------------------------- */
263     if( bResult && !bForceWholeCircle )
264     {
265         poLine->setPoint( poLine->getNumPoints() - 1,
266                           dfEndX, dfEndY );
267     }
268 
269     return bResult;
270 }
271 
272