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: ogrocistroke.cpp ff8146d84de7cba8e09d212d5481ea7d2ede3e98 2017-06-27 20:47:31Z Even Rouault $")
34 
35 /************************************************************************/
36 /*                   OGROCIArcCenterFromEdgePoints()                    */
37 /*                                                                      */
38 /*      Compute the center of an arc/circle from three edge points.     */
39 /************************************************************************/
40 
41 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)42 OGROCIArcCenterFromEdgePoints( double x_c0, double y_c0,
43                                double x_c1, double y_c1,
44                                double x_c2, double y_c2,
45                                double *x_center, double *y_center )
46 
47 {
48 
49 /* -------------------------------------------------------------------- */
50 /*      Handle a degenerate case that occurs in OSNI products by        */
51 /*      making some assumptions.  If the first and third points are     */
52 /*      the same assume they are intended to define a full circle,      */
53 /*      and that the second point is on the opposite side of the        */
54 /*      circle.                                                         */
55 /* -------------------------------------------------------------------- */
56     if( x_c0 == x_c2 && y_c0 == y_c2 )
57     {
58         *x_center = (x_c0 + x_c1) * 0.5;
59         *y_center = (y_c0 + y_c1) * 0.5;
60 
61         return TRUE;
62     }
63 
64 /* -------------------------------------------------------------------- */
65 /*      Compute the inverse of the slopes connecting the first and      */
66 /*      second points.  Also compute the center point of the two        */
67 /*      lines ... the point our crossing line will go through.          */
68 /* -------------------------------------------------------------------- */
69     double m1, x1, y1;
70 
71     if( (y_c1 - y_c0) != 0.0 )
72         m1 = (x_c0 - x_c1) / (y_c1 - y_c0);
73     else
74         m1 = 1e+10;
75 
76     x1 = (x_c0 + x_c1) * 0.5;
77     y1 = (y_c0 + y_c1) * 0.5;
78 
79 /* -------------------------------------------------------------------- */
80 /*      Compute the same for the second point compared to the third     */
81 /*      point.                                                          */
82 /* -------------------------------------------------------------------- */
83     double m2, x2, y2;
84 
85     if( (y_c2 - y_c1) != 0.0 )
86         m2 = (x_c1 - x_c2) / (y_c2 - y_c1);
87     else
88         m2 = 1e+10;
89 
90     x2 = (x_c1 + x_c2) * 0.5;
91     y2 = (y_c1 + y_c2) * 0.5;
92 
93 /* -------------------------------------------------------------------- */
94 /*      Turn these into the Ax+By+C = 0 form of the lines.              */
95 /* -------------------------------------------------------------------- */
96     double      a1, a2, l_b1, l_b2, c1, c2;
97 
98     a1 = m1;
99     a2 = m2;
100 
101     l_b1 = -1.0;
102     l_b2 = -1.0;
103 
104     c1 = (y1 - m1*x1);
105     c2 = (y2 - m2*x2);
106 
107 /* -------------------------------------------------------------------- */
108 /*      Compute the intersection of the two lines through the center    */
109 /*      of the circle, using Kramers rule.                              */
110 /* -------------------------------------------------------------------- */
111     double      det_inv;
112 
113     if( a1*l_b2 - a2*l_b1 == 0.0 )
114         return FALSE;
115 
116     det_inv = 1 / (a1*l_b2 - a2*l_b1);
117 
118     *x_center = (l_b1*c2 - l_b2*c1) * det_inv;
119     *y_center = (a2*c1 - a1*c2) * det_inv;
120 
121     return TRUE;
122 }
123 
124 /************************************************************************/
125 /*                OGROCIStrokeArcToOGRGeometry_Angles()                 */
126 /************************************************************************/
127 
128 static int
OGROCIStrokeArcToOGRGeometry_Angles(double dfCenterX,double dfCenterY,double dfRadius,double dfStartAngle,double dfEndAngle,double dfMaxAngleStepSizeDegrees,OGRLineString * poLine)129 OGROCIStrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY,
130                                      double dfRadius,
131                                      double dfStartAngle, double dfEndAngle,
132                                      double dfMaxAngleStepSizeDegrees,
133                                      OGRLineString *poLine )
134 
135 {
136     double             dfArcX, dfArcY, dfSlice;
137     int                iPoint, iAppendLocation, nVertexCount;
138     double             dfEps = dfRadius / 100000.0;
139 
140     nVertexCount = (int)
141         ceil(fabs(dfEndAngle - dfStartAngle)/dfMaxAngleStepSizeDegrees) + 1;
142     nVertexCount = MAX(2,nVertexCount);
143     dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1);
144     iAppendLocation = poLine->getNumPoints();
145 
146     for( iPoint=0; iPoint < nVertexCount; iPoint++ )
147     {
148         double      dfAngle;
149 
150         dfAngle = (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0;
151 
152         dfArcX = dfCenterX + cos(dfAngle) * dfRadius;
153         dfArcY = dfCenterY + sin(dfAngle) * dfRadius;
154 
155         if( iPoint == 0 )
156         {
157             if( poLine->getNumPoints() > 0
158                 && fabs(poLine->getX(poLine->getNumPoints()-1)-dfArcX) < dfEps
159                 && fabs(poLine->getY(poLine->getNumPoints()-1)-dfArcY) < dfEps)
160             {
161                 poLine->setNumPoints(
162                     poLine->getNumPoints() + nVertexCount - 1 );
163             }
164             else
165             {
166                 poLine->setNumPoints(
167                     poLine->getNumPoints() + nVertexCount - 1 );
168                 poLine->setPoint( iAppendLocation++, dfArcX, dfArcY );
169             }
170         }
171         else
172             poLine->setPoint( iAppendLocation++, dfArcX, dfArcY );
173     }
174 
175     return TRUE;
176 }
177 
178 /************************************************************************/
179 /*                OGROCIStrokeArcToOGRGeometry_Points()                 */
180 /************************************************************************/
181 
182 int
OGROCIStrokeArcToOGRGeometry_Points(double dfStartX,double dfStartY,double dfAlongX,double dfAlongY,double dfEndX,double dfEndY,double dfMaxAngleStepSizeDegrees,int bForceWholeCircle,OGRLineString * poLine)183 OGROCIStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY,
184                                      double dfAlongX, double dfAlongY,
185                                      double dfEndX, double dfEndY,
186                                      double dfMaxAngleStepSizeDegrees,
187                                      int bForceWholeCircle,
188                                      OGRLineString *poLine )
189 
190 {
191     double      dfStartAngle, dfEndAngle, dfAlongAngle;
192     double      dfCenterX, dfCenterY, dfRadius;
193 
194     if( !OGROCIArcCenterFromEdgePoints( dfStartX, dfStartY,
195                                         dfAlongX, dfAlongY,
196                                         dfEndX, dfEndY,
197                                         &dfCenterX, &dfCenterY ) )
198         return FALSE;
199 
200     if( bForceWholeCircle || (dfStartX == dfEndX && dfStartY == dfEndY) )
201     {
202         dfStartAngle = 0.0;
203         dfEndAngle = 360.0;
204     }
205     else
206     {
207         double  dfDeltaX, dfDeltaY;
208 
209         dfDeltaX = dfStartX - dfCenterX;
210         dfDeltaY = dfStartY - dfCenterY;
211         dfStartAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / M_PI;
212 
213         dfDeltaX = dfAlongX - dfCenterX;
214         dfDeltaY = dfAlongY - dfCenterY;
215         dfAlongAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / M_PI;
216 
217         dfDeltaX = dfEndX - dfCenterX;
218         dfDeltaY = dfEndY - dfCenterY;
219         dfEndAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / M_PI;
220 
221         // Try positive (clockwise?) winding.
222         while( dfAlongAngle < dfStartAngle )
223             dfAlongAngle += 360.0;
224 
225         while( dfEndAngle < dfAlongAngle )
226             dfEndAngle += 360.0;
227 
228         // If that doesn't work out, then go anticlockwise.
229         if( dfEndAngle - dfStartAngle > 360.0 )
230         {
231             while( dfAlongAngle > dfStartAngle )
232                 dfAlongAngle -= 360.0;
233 
234             while( dfEndAngle > dfAlongAngle )
235                 dfEndAngle -= 360.0;
236         }
237     }
238 
239     dfRadius = sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX)
240                      + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) );
241 
242     int bResult;
243 
244     bResult =
245         OGROCIStrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY,
246                                              dfRadius,
247                                              dfStartAngle, dfEndAngle,
248                                              dfMaxAngleStepSizeDegrees,
249                                              poLine );
250 
251 /* -------------------------------------------------------------------- */
252 /*      Force the points for arcs, to avoid odd rounding/math           */
253 /*      issues.  Perhaps we should do this for the start too, but       */
254 /*      this is a bit tricky since it isn't obvious which point is      */
255 /*      the start.                                                      */
256 /* -------------------------------------------------------------------- */
257     if( bResult && !bForceWholeCircle )
258     {
259         poLine->setPoint( poLine->getNumPoints() - 1,
260                           dfEndX, dfEndY );
261     }
262 
263     return bResult;
264 }
265