1 : /******************************************************************************
2 : * $Id: contour.cpp 24133 2012-03-19 03:55:58Z chaitanya $
3 : *
4 : * Project: Contour Generation
5 : * Purpose: Core algorithm implementation for contour line generation.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2003, Applied Coherent Technology Corporation, www.actgate.com
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "gdal_priv.h"
32 : #include "gdal_alg.h"
33 : #include "ogr_api.h"
34 :
35 : CPL_CVSID("$Id: contour.cpp 24133 2012-03-19 03:55:58Z chaitanya $");
36 :
37 : #ifdef OGR_ENABLED
38 :
39 : // The amount of a contour interval that pixels should be fudged by if they
40 : // match a contour level exactly.
41 :
42 : #define FUDGE_EXACT 0.001
43 :
44 : // The amount of a pixel that line ends need to be within to be considered to
45 : // match for joining purposes.
46 :
47 : #define JOIN_DIST 0.0001
48 :
49 : /************************************************************************/
50 : /* GDALContourItem */
51 : /************************************************************************/
52 : class GDALContourItem
53 : {
54 : public:
55 : int bRecentlyAccessed;
56 : double dfLevel;
57 :
58 : int nPoints;
59 : int nMaxPoints;
60 : double *padfX;
61 : double *padfY;
62 :
63 : int bLeftIsHigh;
64 :
65 : double dfTailX;
66 :
67 : GDALContourItem( double dfLevel );
68 : ~GDALContourItem();
69 :
70 : int AddSegment( double dfXStart, double dfYStart,
71 : double dfXEnd, double dfYEnd, int bLeftHigh );
72 : void MakeRoomFor( int );
73 : int Merge( GDALContourItem * );
74 : void PrepareEjection();
75 : };
76 :
77 : /************************************************************************/
78 : /* GDALContourLevel */
79 : /************************************************************************/
80 : class GDALContourLevel
81 : {
82 : double dfLevel;
83 :
84 : int nEntryMax;
85 : int nEntryCount;
86 : GDALContourItem **papoEntries;
87 :
88 : public:
89 : GDALContourLevel( double );
90 : ~GDALContourLevel();
91 :
92 575872 : double GetLevel() { return dfLevel; }
93 400488 : int GetContourCount() { return nEntryCount; }
94 329690 : GDALContourItem *GetContour( int i) { return papoEntries[i]; }
95 : void AdjustContour( int );
96 : void RemoveContour( int );
97 : int FindContour( double dfX, double dfY );
98 : int InsertContour( GDALContourItem * );
99 : };
100 :
101 : /************************************************************************/
102 : /* GDALContourGenerator */
103 : /************************************************************************/
104 : class GDALContourGenerator
105 : {
106 : int nWidth;
107 : int nHeight;
108 : int iLine;
109 :
110 : double *padfLastLine;
111 : double *padfThisLine;
112 :
113 : int nLevelMax;
114 : int nLevelCount;
115 : GDALContourLevel **papoLevels;
116 :
117 : int iLastLevel;
118 :
119 : int bNoDataActive;
120 : double dfNoDataValue;
121 :
122 : int bFixedLevels;
123 : double dfContourInterval;
124 : double dfContourOffset;
125 :
126 : CPLErr AddSegment( double dfLevel,
127 : double dfXStart, double dfYStart,
128 : double dfXEnd, double dfYEnd, int bLeftHigh );
129 :
130 : CPLErr ProcessPixel( int iPixel );
131 : CPLErr ProcessRect( double, double, double,
132 : double, double, double,
133 : double, double, double,
134 : double, double, double );
135 :
136 : void Intersect( double, double, double,
137 : double, double, double,
138 : double, double, int *, double *, double * );
139 :
140 : GDALContourLevel *FindLevel( double dfLevel );
141 :
142 : public:
143 : GDALContourWriter pfnWriter;
144 : void *pWriterCBData;
145 :
146 : GDALContourGenerator( int nWidth, int nHeight,
147 : GDALContourWriter pfnWriter, void *pWriterCBData );
148 : ~GDALContourGenerator();
149 :
150 : void SetNoData( double dfNoDataValue );
151 12 : void SetContourLevels( double dfContourInterval,
152 : double dfContourOffset = 0.0 )
153 12 : { this->dfContourInterval = dfContourInterval;
154 12 : this->dfContourOffset = dfContourOffset; }
155 :
156 : void SetFixedLevels( int, double * );
157 : CPLErr FeedLine( double *padfScanline );
158 : CPLErr EjectContours( int bOnlyUnused = FALSE );
159 :
160 : };
161 :
162 : /************************************************************************/
163 : /* GDAL_CG_Create() */
164 : /************************************************************************/
165 :
166 : GDALContourGeneratorH
167 0 : GDAL_CG_Create( int nWidth, int nHeight, int bNoDataSet, double dfNoDataValue,
168 : double dfContourInterval, double dfContourBase,
169 : GDALContourWriter pfnWriter, void *pCBData )
170 :
171 : {
172 : GDALContourGenerator *poCG = new GDALContourGenerator( nWidth, nHeight,
173 0 : pfnWriter, pCBData );
174 :
175 0 : if( bNoDataSet )
176 0 : poCG->SetNoData( dfNoDataValue );
177 :
178 0 : poCG->SetContourLevels( dfContourInterval, dfContourBase );
179 0 : return (GDALContourGeneratorH) poCG;
180 : }
181 :
182 : /************************************************************************/
183 : /* GDAL_CG_FeedLine() */
184 : /************************************************************************/
185 :
186 0 : CPLErr GDAL_CG_FeedLine( GDALContourGeneratorH hCG, double *padfScanline )
187 :
188 : {
189 0 : VALIDATE_POINTER1( hCG, "GDAL_CG_FeedLine", CE_Failure );
190 :
191 0 : return ((GDALContourGenerator *) hCG)->FeedLine( padfScanline );
192 : }
193 :
194 : /************************************************************************/
195 : /* GDAL_CG_Destroy() */
196 : /************************************************************************/
197 :
198 0 : void GDAL_CG_Destroy( GDALContourGeneratorH hCG )
199 :
200 : {
201 0 : delete ((GDALContourGenerator *) hCG);
202 0 : }
203 :
204 : /************************************************************************/
205 : /* ==================================================================== */
206 : /* GDALContourGenerator */
207 : /* ==================================================================== */
208 : /************************************************************************/
209 :
210 : /************************************************************************/
211 : /* GDALContourGenerator() */
212 : /************************************************************************/
213 :
214 16 : GDALContourGenerator::GDALContourGenerator( int nWidthIn, int nHeightIn,
215 : GDALContourWriter pfnWriterIn,
216 : void *pWriterCBDataIn )
217 : {
218 16 : nWidth = nWidthIn;
219 16 : nHeight = nHeightIn;
220 :
221 16 : padfLastLine = (double *) CPLCalloc(sizeof(double),nWidth);
222 16 : padfThisLine = (double *) CPLCalloc(sizeof(double),nWidth);
223 :
224 16 : pfnWriter = pfnWriterIn;
225 16 : pWriterCBData = pWriterCBDataIn;
226 :
227 16 : iLine = -1;
228 :
229 16 : bNoDataActive = FALSE;
230 16 : dfNoDataValue = -1000000.0;
231 16 : dfContourInterval = 10.0;
232 16 : dfContourOffset = 0.0;
233 :
234 16 : nLevelMax = 0;
235 16 : nLevelCount = 0;
236 16 : papoLevels = NULL;
237 16 : bFixedLevels = FALSE;
238 16 : }
239 :
240 : /************************************************************************/
241 : /* ~GDALContourGenerator() */
242 : /************************************************************************/
243 :
244 16 : GDALContourGenerator::~GDALContourGenerator()
245 :
246 : {
247 : int i;
248 :
249 136 : for( i = 0; i < nLevelCount; i++ )
250 120 : delete papoLevels[i];
251 16 : CPLFree( papoLevels );
252 :
253 16 : CPLFree( padfLastLine );
254 16 : CPLFree( padfThisLine );
255 16 : }
256 :
257 : /************************************************************************/
258 : /* SetFixedLevels() */
259 : /************************************************************************/
260 :
261 4 : void GDALContourGenerator::SetFixedLevels( int nFixedLevelCount,
262 : double *padfFixedLevels )
263 :
264 : {
265 4 : bFixedLevels = TRUE;
266 16 : for( int i = 0; i < nFixedLevelCount; i++ )
267 12 : FindLevel( padfFixedLevels[i] );
268 4 : }
269 :
270 : /************************************************************************/
271 : /* SetNoData() */
272 : /************************************************************************/
273 :
274 6 : void GDALContourGenerator::SetNoData( double dfNewValue )
275 :
276 : {
277 6 : bNoDataActive = TRUE;
278 6 : dfNoDataValue = dfNewValue;
279 6 : }
280 :
281 : /************************************************************************/
282 : /* ProcessPixel() */
283 : /************************************************************************/
284 :
285 318806 : CPLErr GDALContourGenerator::ProcessPixel( int iPixel )
286 :
287 : {
288 : double dfUpLeft, dfUpRight, dfLoLeft, dfLoRight;
289 318806 : int bSubdivide = FALSE;
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Collect the four corner pixel values. Value left or right */
293 : /* of the scanline are taken from the nearest pixel on the */
294 : /* scanline itself. */
295 : /* -------------------------------------------------------------------- */
296 318806 : dfUpLeft = padfLastLine[MAX(0,iPixel-1)];
297 318806 : dfUpRight = padfLastLine[MIN(nWidth-1,iPixel)];
298 :
299 318806 : dfLoLeft = padfThisLine[MAX(0,iPixel-1)];
300 318806 : dfLoRight = padfThisLine[MIN(nWidth-1,iPixel)];
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Check if we have any nodata values. */
304 : /* -------------------------------------------------------------------- */
305 318806 : if( bNoDataActive
306 : && ( dfUpLeft == dfNoDataValue
307 : || dfLoLeft == dfNoDataValue
308 : || dfLoRight == dfNoDataValue
309 : || dfUpRight == dfNoDataValue ) )
310 0 : bSubdivide = TRUE;
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Check if we have any nodata, if so, go to a special case of */
314 : /* code. */
315 : /* -------------------------------------------------------------------- */
316 318806 : if( iPixel > 0 && iPixel < nWidth
317 : && iLine > 0 && iLine < nHeight && !bSubdivide )
318 : {
319 : return ProcessRect( dfUpLeft, iPixel - 0.5, iLine - 0.5,
320 : dfLoLeft, iPixel - 0.5, iLine + 0.5,
321 : dfLoRight, iPixel + 0.5, iLine + 0.5,
322 310434 : dfUpRight, iPixel + 0.5, iLine - 0.5 );
323 : }
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Prepare subdivisions. */
327 : /* -------------------------------------------------------------------- */
328 8372 : int nGoodCount = 0;
329 8372 : double dfASum = 0.0;
330 8372 : double dfCenter, dfTop=0.0, dfRight=0.0, dfLeft=0.0, dfBottom=0.0;
331 :
332 8372 : if( dfUpLeft != dfNoDataValue )
333 : {
334 8372 : dfASum += dfUpLeft;
335 8372 : nGoodCount++;
336 : }
337 :
338 8372 : if( dfLoLeft != dfNoDataValue )
339 : {
340 8372 : dfASum += dfLoLeft;
341 8372 : nGoodCount++;
342 : }
343 :
344 8372 : if( dfLoRight != dfNoDataValue )
345 : {
346 8372 : dfASum += dfLoRight;
347 8372 : nGoodCount++;
348 : }
349 :
350 8372 : if( dfUpRight != dfNoDataValue )
351 : {
352 8372 : dfASum += dfUpRight;
353 8372 : nGoodCount++;
354 : }
355 :
356 8372 : if( nGoodCount == 0.0 )
357 0 : return CE_None;
358 :
359 8372 : dfCenter = dfASum / nGoodCount;
360 :
361 8372 : if( dfUpLeft != dfNoDataValue )
362 : {
363 8372 : if( dfUpRight != dfNoDataValue )
364 8372 : dfTop = (dfUpLeft + dfUpRight) / 2.0;
365 : else
366 0 : dfTop = dfUpLeft;
367 :
368 8372 : if( dfLoLeft != dfNoDataValue )
369 8372 : dfLeft = (dfUpLeft + dfLoLeft) / 2.0;
370 : else
371 0 : dfLeft = dfUpLeft;
372 : }
373 : else
374 : {
375 0 : dfTop = dfUpRight;
376 0 : dfLeft = dfLoLeft;
377 : }
378 :
379 8372 : if( dfLoRight != dfNoDataValue )
380 : {
381 8372 : if( dfUpRight != dfNoDataValue )
382 8372 : dfRight = (dfLoRight + dfUpRight) / 2.0;
383 : else
384 0 : dfRight = dfLoRight;
385 :
386 8372 : if( dfLoLeft != dfNoDataValue )
387 8372 : dfBottom = (dfLoRight + dfLoLeft) / 2.0;
388 : else
389 0 : dfBottom = dfLoRight;
390 : }
391 : else
392 : {
393 0 : dfBottom = dfLoLeft;;
394 0 : dfRight = dfUpRight;
395 : }
396 :
397 : /* -------------------------------------------------------------------- */
398 : /* Process any quadrants that aren't "nodata" anchored. */
399 : /* -------------------------------------------------------------------- */
400 8372 : CPLErr eErr = CE_None;
401 :
402 8372 : if( dfUpLeft != dfNoDataValue && iPixel > 0 && iLine > 0 )
403 : {
404 : eErr = ProcessRect( dfUpLeft, iPixel - 0.5, iLine - 0.5,
405 : dfLeft, iPixel - 0.5, iLine,
406 : dfCenter, iPixel, iLine,
407 4170 : dfTop, iPixel, iLine - 0.5 );
408 : }
409 :
410 8372 : if( dfLoLeft != dfNoDataValue && eErr == CE_None
411 : && iPixel > 0 && iLine < nHeight )
412 : {
413 : eErr = ProcessRect( dfLeft, iPixel - 0.5, iLine,
414 : dfLoLeft, iPixel - 0.5, iLine + 0.5,
415 : dfBottom, iPixel, iLine + 0.5,
416 4170 : dfCenter, iPixel, iLine );
417 : }
418 :
419 8372 : if( dfLoRight != dfNoDataValue && iPixel < nWidth && iLine < nHeight )
420 : {
421 : eErr = ProcessRect( dfCenter, iPixel, iLine,
422 : dfBottom, iPixel, iLine + 0.5,
423 : dfLoRight, iPixel + 0.5, iLine + 0.5,
424 4170 : dfRight, iPixel + 0.5, iLine );
425 : }
426 :
427 8372 : if( dfUpRight != dfNoDataValue && iPixel < nWidth && iLine > 0 )
428 : {
429 : eErr = ProcessRect( dfTop, iPixel, iLine - 0.5,
430 : dfCenter, iPixel, iLine,
431 : dfRight, iPixel + 0.5, iLine,
432 4170 : dfUpRight, iPixel + 0.5, iLine - 0.5 );
433 : }
434 :
435 8372 : return eErr;
436 : }
437 :
438 : /************************************************************************/
439 : /* ProcessRect() */
440 : /************************************************************************/
441 :
442 327114 : CPLErr GDALContourGenerator::ProcessRect(
443 : double dfUpLeft, double dfUpLeftX, double dfUpLeftY,
444 : double dfLoLeft, double dfLoLeftX, double dfLoLeftY,
445 : double dfLoRight, double dfLoRightX, double dfLoRightY,
446 : double dfUpRight, double dfUpRightX, double dfUpRightY )
447 :
448 : {
449 : /* -------------------------------------------------------------------- */
450 : /* Identify the range of elevations over this rect. */
451 : /* -------------------------------------------------------------------- */
452 : int iStartLevel, iEndLevel;
453 :
454 327114 : double dfMin = MIN(MIN(dfUpLeft,dfUpRight),MIN(dfLoLeft,dfLoRight));
455 327114 : double dfMax = MAX(MAX(dfUpLeft,dfUpRight),MAX(dfLoLeft,dfLoRight));
456 :
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Compute the set of levels to compute contours for. */
460 : /* -------------------------------------------------------------------- */
461 :
462 : /*
463 : ** If we are using fixed levels, then find the min/max in the levels
464 : ** table.
465 : */
466 327114 : if( bFixedLevels )
467 : {
468 106228 : int nStart=0, nEnd=nLevelCount-1, nMiddle;
469 :
470 106228 : iStartLevel = -1;
471 423468 : while( nStart <= nEnd )
472 : {
473 212456 : nMiddle = (nEnd + nStart) / 2;
474 :
475 212456 : double dfMiddleLevel = papoLevels[nMiddle]->GetLevel();
476 :
477 212456 : if( dfMiddleLevel < dfMin )
478 24964 : nStart = nMiddle + 1;
479 187492 : else if( dfMiddleLevel > dfMin )
480 186048 : nEnd = nMiddle - 1;
481 : else
482 : {
483 1444 : iStartLevel = nMiddle;
484 1444 : break;
485 : }
486 : }
487 :
488 106228 : if( iStartLevel == -1 )
489 104784 : iStartLevel = nEnd + 1;
490 :
491 106228 : iEndLevel = iStartLevel;
492 312600 : while( iEndLevel < nLevelCount-1
493 100144 : && papoLevels[iEndLevel+1]->GetLevel() < dfMax )
494 0 : iEndLevel++;
495 :
496 106228 : if( iStartLevel >= nLevelCount )
497 0 : return CE_None;
498 :
499 106228 : CPLAssert( iStartLevel >= 0 && iStartLevel < nLevelCount );
500 106228 : CPLAssert( iEndLevel >= 0 && iEndLevel < nLevelCount );
501 : }
502 :
503 : /*
504 : ** Otherwise figure out the start and end using the base and offset.
505 : */
506 : else
507 : {
508 : iStartLevel = (int)
509 220886 : ceil((dfMin - dfContourOffset) / dfContourInterval);
510 : iEndLevel = (int)
511 220886 : floor((dfMax - dfContourOffset) / dfContourInterval);
512 : }
513 :
514 327114 : if( iStartLevel > iEndLevel )
515 196666 : return CE_None;
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* Loop over them. */
519 : /* -------------------------------------------------------------------- */
520 : int iLevel;
521 :
522 275278 : for( iLevel = iStartLevel; iLevel <= iEndLevel; iLevel++ )
523 : {
524 : double dfLevel;
525 :
526 144830 : if( bFixedLevels )
527 106228 : dfLevel = papoLevels[iLevel]->GetLevel();
528 : else
529 38602 : dfLevel = iLevel * dfContourInterval + dfContourOffset;
530 :
531 144830 : int nPoints = 0;
532 : double adfX[4], adfY[4];
533 144830 : CPLErr eErr = CE_None;
534 :
535 : /* Logs how many points we have af left + bottom,
536 : ** and left + bottom + right.
537 : */
538 144830 : int nPoints1 = 0, nPoints2 = 0, nPoints3 = 0;
539 :
540 :
541 : Intersect( dfUpLeft, dfUpLeftX, dfUpLeftY,
542 : dfLoLeft, dfLoLeftX, dfLoLeftY,
543 144830 : dfLoRight, dfLevel, &nPoints, adfX, adfY );
544 144830 : nPoints1 = nPoints;
545 : Intersect( dfLoLeft, dfLoLeftX, dfLoLeftY,
546 : dfLoRight, dfLoRightX, dfLoRightY,
547 144830 : dfUpRight, dfLevel, &nPoints, adfX, adfY );
548 144830 : nPoints2 = nPoints;
549 : Intersect( dfLoRight, dfLoRightX, dfLoRightY,
550 : dfUpRight, dfUpRightX, dfUpRightY,
551 144830 : dfUpLeft, dfLevel, &nPoints, adfX, adfY );
552 144830 : nPoints3 = nPoints;
553 : Intersect( dfUpRight, dfUpRightX, dfUpRightY,
554 : dfUpLeft, dfUpLeftX, dfUpLeftY,
555 144830 : dfLoLeft, dfLevel, &nPoints, adfX, adfY );
556 :
557 144830 : if( nPoints == 1 || nPoints == 3 )
558 16 : CPLDebug( "CONTOUR", "Got nPoints = %d", nPoints );
559 :
560 144830 : if( nPoints >= 2 )
561 : {
562 47060 : if ( nPoints1 == 1 && nPoints2 == 2) // left + bottom
563 : {
564 : eErr = AddSegment( dfLevel,
565 : adfX[0], adfY[0], adfX[1], adfY[1],
566 6234 : dfUpRight > dfLoLeft );
567 : }
568 45762 : else if ( nPoints1 == 1 && nPoints3 == 2 ) // left + right
569 : {
570 : eErr = AddSegment( dfLevel,
571 : adfX[0], adfY[0], adfX[1], adfY[1],
572 11170 : dfUpLeft > dfLoRight );
573 : }
574 29774 : else if ( nPoints1 == 1 && nPoints == 2 ) // left + top
575 : { // Do not do vertical contours on the left, due to symmetry
576 6352 : if ( !(dfUpLeft == dfLevel && dfLoLeft == dfLevel) )
577 : eErr = AddSegment( dfLevel,
578 : adfX[0], adfY[0], adfX[1], adfY[1],
579 6230 : dfUpLeft > dfLoRight );
580 : }
581 23222 : else if( nPoints2 == 1 && nPoints3 == 2) // bottom + right
582 : {
583 : eErr = AddSegment( dfLevel,
584 : adfX[0], adfY[0], adfX[1], adfY[1],
585 6152 : dfUpLeft > dfLoRight );
586 : }
587 16554 : else if ( nPoints2 == 1 && nPoints == 2 ) // bottom + top
588 : {
589 : eErr = AddSegment( dfLevel,
590 : adfX[0], adfY[0], adfX[1], adfY[1],
591 5636 : dfLoLeft > dfUpRight );
592 : }
593 10564 : else if ( nPoints3 == 1 && nPoints == 2 ) // right + top
594 : { // Do not do horizontal contours on upside, due to symmetry
595 5282 : if ( !(dfUpRight == dfLevel && dfUpLeft == dfLevel) )
596 : eErr = AddSegment( dfLevel,
597 : adfX[0], adfY[0], adfX[1], adfY[1],
598 5172 : dfLoLeft > dfUpRight );
599 : }
600 : else
601 : {
602 : // If we get here it is a serious error!
603 0 : CPLDebug( "CONTOUR", "Contour state not implemented!");
604 : }
605 :
606 40826 : if( eErr != CE_None )
607 0 : return eErr;
608 : }
609 :
610 144830 : if( nPoints == 4 )
611 : {
612 : // Do not do horizontal contours on upside, due to symmetry
613 922 : if ( !(dfUpRight == dfLevel && dfUpLeft == dfLevel) )
614 : {
615 : /* -------------------------------------------------------------------- */
616 : /* If we get here, we know the first was left+bottom, */
617 : /* so we are at right+top, therefore "left is high" */
618 : /* if low-left is larger than up-right. */
619 : /* We do not do a diagonal check here as we are dealing with */
620 : /* a saddle point. */
621 : /* -------------------------------------------------------------------- */
622 : eErr = AddSegment( dfLevel,
623 : adfX[2], adfY[2], adfX[3], adfY[3],
624 922 : ( dfLoRight > dfUpRight) );
625 922 : if( eErr != CE_None )
626 0 : return eErr;
627 : }
628 : }
629 : }
630 :
631 130448 : return CE_None;
632 : }
633 :
634 : /************************************************************************/
635 : /* Intersect() */
636 : /************************************************************************/
637 :
638 579320 : void GDALContourGenerator::Intersect( double dfVal1, double dfX1, double dfY1,
639 : double dfVal2, double dfX2, double dfY2,
640 : double dfNext,
641 : double dfLevel, int *pnPoints,
642 : double *padfX, double *padfY )
643 :
644 : {
645 621004 : if( dfVal1 < dfLevel && dfVal2 >= dfLevel )
646 : {
647 41684 : double dfRatio = (dfLevel - dfVal1) / (dfVal2 - dfVal1);
648 :
649 41684 : padfX[*pnPoints] = dfX1 * (1.0 - dfRatio) + dfX2 * dfRatio;
650 41684 : padfY[*pnPoints] = dfY1 * (1.0 - dfRatio) + dfY2 * dfRatio;
651 41684 : (*pnPoints)++;
652 : }
653 579000 : else if( dfVal1 > dfLevel && dfVal2 <= dfLevel )
654 : {
655 41364 : double dfRatio = (dfLevel - dfVal2) / (dfVal1 - dfVal2);
656 :
657 41364 : padfX[*pnPoints] = dfX2 * (1.0 - dfRatio) + dfX1 * dfRatio;
658 41364 : padfY[*pnPoints] = dfY2 * (1.0 - dfRatio) + dfY1 * dfRatio;
659 41364 : (*pnPoints)++;
660 : }
661 496272 : else if( dfVal1 == dfLevel && dfVal2 == dfLevel && dfNext != dfLevel )
662 : {
663 464 : padfX[*pnPoints] = dfX2;
664 464 : padfY[*pnPoints] = dfY2;
665 464 : (*pnPoints)++;
666 : }
667 579320 : }
668 :
669 : /************************************************************************/
670 : /* AddSegment() */
671 : /************************************************************************/
672 :
673 41516 : CPLErr GDALContourGenerator::AddSegment( double dfLevel,
674 : double dfX1, double dfY1,
675 : double dfX2, double dfY2,
676 : int bLeftHigh)
677 :
678 : {
679 41516 : GDALContourLevel *poLevel = FindLevel( dfLevel );
680 : GDALContourItem *poTarget;
681 : int iTarget;
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Check all active contours for any that this might attach */
685 : /* to. Eventually this should be recoded to find the contours */
686 : /* of the correct level more efficiently. */
687 : /* -------------------------------------------------------------------- */
688 :
689 41516 : if( dfY1 < dfY2 )
690 10414 : iTarget = poLevel->FindContour( dfX1, dfY1 );
691 : else
692 31102 : iTarget = poLevel->FindContour( dfX2, dfY2 );
693 :
694 41516 : if( iTarget != -1 )
695 : {
696 296 : poTarget = poLevel->GetContour( iTarget );
697 :
698 296 : poTarget->AddSegment( dfX1, dfY1, dfX2, dfY2, bLeftHigh );
699 :
700 296 : poLevel->AdjustContour( iTarget );
701 :
702 296 : return CE_None;
703 : }
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* No existing contour found, lets create a new one. */
707 : /* -------------------------------------------------------------------- */
708 41220 : poTarget = new GDALContourItem( dfLevel );
709 :
710 41220 : poTarget->AddSegment( dfX1, dfY1, dfX2, dfY2, bLeftHigh );
711 :
712 41220 : poLevel->InsertContour( poTarget );
713 :
714 41220 : return CE_None;
715 : }
716 :
717 : /************************************************************************/
718 : /* FeedLine() */
719 : /************************************************************************/
720 :
721 2110 : CPLErr GDALContourGenerator::FeedLine( double *padfScanline )
722 :
723 : {
724 : /* -------------------------------------------------------------------- */
725 : /* Switch current line to "lastline" slot, and copy new data */
726 : /* into new "this line". */
727 : /* -------------------------------------------------------------------- */
728 2110 : double *padfTempLine = padfLastLine;
729 2110 : padfLastLine = padfThisLine;
730 2110 : padfThisLine = padfTempLine;
731 :
732 : /* -------------------------------------------------------------------- */
733 : /* If this is the end of the lines (NULL passed in), copy the */
734 : /* last line. */
735 : /* -------------------------------------------------------------------- */
736 2110 : if( padfScanline == NULL )
737 : {
738 16 : memcpy( padfThisLine, padfLastLine, sizeof(double) * nWidth );
739 : }
740 : else
741 : {
742 2094 : memcpy( padfThisLine, padfScanline, sizeof(double) * nWidth );
743 : }
744 :
745 : /* -------------------------------------------------------------------- */
746 : /* Perturb any values that occur exactly on level boundaries. */
747 : /* -------------------------------------------------------------------- */
748 : int iPixel;
749 :
750 318806 : for( iPixel = 0; iPixel < nWidth; iPixel++ )
751 : {
752 316696 : if( bNoDataActive && padfThisLine[iPixel] == dfNoDataValue )
753 0 : continue;
754 :
755 316696 : double dfLevel = (padfThisLine[iPixel] - dfContourOffset)
756 316696 : / dfContourInterval;
757 :
758 316696 : if( dfLevel - (int) dfLevel == 0.0 )
759 : {
760 204048 : padfThisLine[iPixel] += dfContourInterval * FUDGE_EXACT;
761 : }
762 : }
763 :
764 : /* -------------------------------------------------------------------- */
765 : /* If this is the first line we need to initialize the previous */
766 : /* line from the first line of data. */
767 : /* -------------------------------------------------------------------- */
768 2110 : if( iLine == -1 )
769 : {
770 16 : memcpy( padfLastLine, padfThisLine, sizeof(double) * nWidth );
771 16 : iLine = 0;
772 : }
773 :
774 : /* -------------------------------------------------------------------- */
775 : /* Clear the recently used flags on the contours so we can */
776 : /* check later which ones were touched for this scanline. */
777 : /* -------------------------------------------------------------------- */
778 : int iLevel, iContour;
779 :
780 16236 : for( iLevel = 0; iLevel < nLevelCount; iLevel++ )
781 : {
782 14126 : GDALContourLevel *poLevel = papoLevels[iLevel];
783 :
784 71118 : for( iContour = 0; iContour < poLevel->GetContourCount(); iContour++ )
785 56992 : poLevel->GetContour( iContour )->bRecentlyAccessed = FALSE;
786 : }
787 :
788 : /* -------------------------------------------------------------------- */
789 : /* Process each pixel. */
790 : /* -------------------------------------------------------------------- */
791 320916 : for( iPixel = 0; iPixel < nWidth+1; iPixel++ )
792 : {
793 318806 : CPLErr eErr = ProcessPixel( iPixel );
794 318806 : if( eErr != CE_None )
795 0 : return eErr;
796 : }
797 :
798 : /* -------------------------------------------------------------------- */
799 : /* eject any pending contours. */
800 : /* -------------------------------------------------------------------- */
801 2110 : CPLErr eErr = EjectContours( padfScanline != NULL );
802 :
803 2110 : iLine++;
804 :
805 2110 : if( iLine == nHeight && eErr == CE_None )
806 16 : return FeedLine( NULL );
807 : else
808 2094 : return eErr;
809 : }
810 :
811 : /************************************************************************/
812 : /* EjectContours() */
813 : /************************************************************************/
814 :
815 2110 : CPLErr GDALContourGenerator::EjectContours( int bOnlyUnused )
816 :
817 : {
818 : int iLevel;
819 2110 : CPLErr eErr = CE_None;
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Process all contours of all levels that match our criteria */
823 : /* -------------------------------------------------------------------- */
824 16344 : for( iLevel = 0; iLevel < nLevelCount && eErr == CE_None; iLevel++ )
825 : {
826 14234 : GDALContourLevel *poLevel = papoLevels[iLevel];
827 : int iContour;
828 :
829 126680 : for( iContour = 0;
830 : iContour < poLevel->GetContourCount() && eErr == CE_None;
831 : /* increment in loop if we don't consume it. */ )
832 : {
833 : int iC2;
834 98212 : GDALContourItem *poTarget = poLevel->GetContour( iContour );
835 :
836 98212 : if( bOnlyUnused && poTarget->bRecentlyAccessed )
837 : {
838 56992 : iContour++;
839 56992 : continue;
840 : }
841 :
842 41220 : poLevel->RemoveContour( iContour );
843 :
844 : // Try to find another contour we can merge with in this level.
845 :
846 175704 : for( iC2 = 0; iC2 < poLevel->GetContourCount(); iC2++ )
847 : {
848 174190 : GDALContourItem *poOther = poLevel->GetContour( iC2 );
849 :
850 174190 : if( poOther->Merge( poTarget ) )
851 39706 : break;
852 : }
853 :
854 : // If we didn't merge it, then eject (write) it out.
855 41220 : if( iC2 == poLevel->GetContourCount() )
856 : {
857 1514 : if( pfnWriter != NULL )
858 : {
859 : // If direction is wrong, then reverse before ejecting.
860 1514 : poTarget->PrepareEjection();
861 :
862 : eErr = pfnWriter( poTarget->dfLevel, poTarget->nPoints,
863 : poTarget->padfX, poTarget->padfY,
864 1514 : pWriterCBData );
865 : }
866 : }
867 :
868 41220 : delete poTarget;
869 : }
870 : }
871 :
872 2110 : return eErr;
873 : }
874 :
875 : /************************************************************************/
876 : /* FindLevel() */
877 : /************************************************************************/
878 :
879 41528 : GDALContourLevel *GDALContourGenerator::FindLevel( double dfLevel )
880 :
881 : {
882 41528 : int nStart=0, nEnd=nLevelCount-1, nMiddle;
883 :
884 : /* -------------------------------------------------------------------- */
885 : /* Binary search to find the requested level. */
886 : /* -------------------------------------------------------------------- */
887 198692 : while( nStart <= nEnd )
888 : {
889 157044 : nMiddle = (nEnd + nStart) / 2;
890 :
891 157044 : double dfMiddleLevel = papoLevels[nMiddle]->GetLevel();
892 :
893 157044 : if( dfMiddleLevel < dfLevel )
894 47950 : nStart = nMiddle + 1;
895 109094 : else if( dfMiddleLevel > dfLevel )
896 67686 : nEnd = nMiddle - 1;
897 : else
898 41408 : return papoLevels[nMiddle];
899 : }
900 :
901 : /* -------------------------------------------------------------------- */
902 : /* Didn't find the level, create a new one and insert it in */
903 : /* order. */
904 : /* -------------------------------------------------------------------- */
905 120 : GDALContourLevel *poLevel = new GDALContourLevel( dfLevel );
906 :
907 120 : if( nLevelMax == nLevelCount )
908 : {
909 20 : nLevelMax = nLevelMax * 2 + 10;
910 : papoLevels = (GDALContourLevel **)
911 20 : CPLRealloc( papoLevels, sizeof(void*) * nLevelMax );
912 : }
913 :
914 120 : if( nLevelCount - nEnd - 1 > 0 )
915 : memmove( papoLevels + nEnd + 2, papoLevels + nEnd + 1,
916 56 : (nLevelCount - nEnd - 1) * sizeof(void*) );
917 120 : papoLevels[nEnd+1] = poLevel;
918 120 : nLevelCount++;
919 :
920 120 : return poLevel;
921 : }
922 :
923 : /************************************************************************/
924 : /* ==================================================================== */
925 : /* GDALContourLevel */
926 : /* ==================================================================== */
927 : /************************************************************************/
928 :
929 : /************************************************************************/
930 : /* GDALContourLevel() */
931 : /************************************************************************/
932 :
933 120 : GDALContourLevel::GDALContourLevel( double dfLevelIn )
934 :
935 : {
936 120 : dfLevel = dfLevelIn;
937 120 : nEntryMax = 0;
938 120 : nEntryCount = 0;
939 120 : papoEntries = NULL;
940 120 : }
941 :
942 : /************************************************************************/
943 : /* ~GDALContourLevel() */
944 : /************************************************************************/
945 :
946 120 : GDALContourLevel::~GDALContourLevel()
947 :
948 : {
949 120 : CPLAssert( nEntryCount == 0 );
950 120 : CPLFree( papoEntries );
951 120 : }
952 :
953 : /************************************************************************/
954 : /* AdjustContour() */
955 : /* */
956 : /* Assume the indicated contour's tail may have changed, and */
957 : /* adjust it up or down in the list of contours to re-establish */
958 : /* proper ordering. */
959 : /************************************************************************/
960 :
961 296 : void GDALContourLevel::AdjustContour( int iChanged )
962 :
963 : {
964 592 : while( iChanged > 0
965 0 : && papoEntries[iChanged]->dfTailX < papoEntries[iChanged-1]->dfTailX )
966 : {
967 0 : GDALContourItem *poTemp = papoEntries[iChanged];
968 0 : papoEntries[iChanged] = papoEntries[iChanged-1];
969 0 : papoEntries[iChanged-1] = poTemp;
970 0 : iChanged--;
971 : }
972 :
973 822 : while( iChanged < nEntryCount-1
974 212 : && papoEntries[iChanged]->dfTailX > papoEntries[iChanged+1]->dfTailX )
975 : {
976 18 : GDALContourItem *poTemp = papoEntries[iChanged];
977 18 : papoEntries[iChanged] = papoEntries[iChanged+1];
978 18 : papoEntries[iChanged+1] = poTemp;
979 18 : iChanged++;
980 : }
981 296 : }
982 :
983 : /************************************************************************/
984 : /* RemoveContour() */
985 : /************************************************************************/
986 :
987 41220 : void GDALContourLevel::RemoveContour( int iTarget )
988 :
989 : {
990 41220 : if( iTarget < nEntryCount )
991 : memmove( papoEntries + iTarget, papoEntries + iTarget + 1,
992 41220 : (nEntryCount - iTarget - 1) * sizeof(void*) );
993 41220 : nEntryCount--;
994 41220 : }
995 :
996 : /************************************************************************/
997 : /* FindContour() */
998 : /* */
999 : /* Perform a binary search to find the requested "tail" */
1000 : /* location. If not available return -1. In theory there can */
1001 : /* be more than one contour with the same tail X and different */
1002 : /* Y tails ... ensure we check against them all. */
1003 : /************************************************************************/
1004 :
1005 41516 : int GDALContourLevel::FindContour( double dfX, double dfY )
1006 :
1007 : {
1008 41516 : int nStart = 0, nEnd = nEntryCount-1, nMiddle;
1009 :
1010 228908 : while( nEnd >= nStart )
1011 : {
1012 155132 : nMiddle = (nEnd + nStart) / 2;
1013 :
1014 155132 : double dfMiddleX = papoEntries[nMiddle]->dfTailX;
1015 :
1016 155132 : if( dfMiddleX < dfX )
1017 87658 : nStart = nMiddle + 1;
1018 67474 : else if( dfMiddleX > dfX )
1019 58218 : nEnd = nMiddle - 1;
1020 : else
1021 : {
1022 42146 : while( nMiddle > 0
1023 15664 : && fabs(papoEntries[nMiddle]->dfTailX-dfX) < JOIN_DIST )
1024 7970 : nMiddle--;
1025 :
1026 30732 : while( nMiddle < nEntryCount
1027 10266 : && fabs(papoEntries[nMiddle]->dfTailX-dfX) < JOIN_DIST )
1028 : {
1029 2250 : if( fabs(papoEntries[nMiddle]->padfY[papoEntries[nMiddle]->nPoints-1] - dfY) < JOIN_DIST )
1030 296 : return nMiddle;
1031 1954 : nMiddle++;
1032 : }
1033 :
1034 8960 : return -1;
1035 : }
1036 : }
1037 :
1038 32260 : return -1;
1039 : }
1040 :
1041 : /************************************************************************/
1042 : /* InsertContour() */
1043 : /* */
1044 : /* Ensure the newly added contour is placed in order according */
1045 : /* to the X value relative to the other contours. */
1046 : /************************************************************************/
1047 :
1048 41220 : int GDALContourLevel::InsertContour( GDALContourItem *poNewContour )
1049 :
1050 : {
1051 : /* -------------------------------------------------------------------- */
1052 : /* Find where to insert by binary search. */
1053 : /* -------------------------------------------------------------------- */
1054 41220 : int nStart = 0, nEnd = nEntryCount-1, nMiddle;
1055 :
1056 254862 : while( nEnd >= nStart )
1057 : {
1058 172422 : nMiddle = (nEnd + nStart) / 2;
1059 :
1060 172422 : double dfMiddleX = papoEntries[nMiddle]->dfTailX;
1061 :
1062 172422 : if( dfMiddleX < poNewContour->dfLevel )
1063 154558 : nStart = nMiddle + 1;
1064 17864 : else if( dfMiddleX > poNewContour->dfLevel )
1065 17864 : nEnd = nMiddle - 1;
1066 : else
1067 : {
1068 0 : nEnd = nMiddle - 1;
1069 0 : break;
1070 : }
1071 : }
1072 :
1073 : /* -------------------------------------------------------------------- */
1074 : /* Do we need to grow the array? */
1075 : /* -------------------------------------------------------------------- */
1076 41220 : if( nEntryMax == nEntryCount )
1077 : {
1078 320 : nEntryMax = nEntryMax * 2 + 10;
1079 : papoEntries = (GDALContourItem **)
1080 320 : CPLRealloc( papoEntries, sizeof(void*) * nEntryMax );
1081 : }
1082 :
1083 : /* -------------------------------------------------------------------- */
1084 : /* Insert the new contour at the appropriate location. */
1085 : /* -------------------------------------------------------------------- */
1086 41220 : if( nEntryCount - nEnd - 1 > 0 )
1087 : memmove( papoEntries + nEnd + 2, papoEntries + nEnd + 1,
1088 7436 : (nEntryCount - nEnd - 1) * sizeof(void*) );
1089 41220 : papoEntries[nEnd+1] = poNewContour;
1090 41220 : nEntryCount++;
1091 :
1092 41220 : return nEnd+1;
1093 : }
1094 :
1095 :
1096 : /************************************************************************/
1097 : /* ==================================================================== */
1098 : /* GDALContourItem */
1099 : /* ==================================================================== */
1100 : /************************************************************************/
1101 :
1102 : /************************************************************************/
1103 : /* GDALContourItem() */
1104 : /************************************************************************/
1105 :
1106 41220 : GDALContourItem::GDALContourItem( double dfLevelIn )
1107 :
1108 : {
1109 41220 : dfLevel = dfLevelIn;
1110 41220 : bRecentlyAccessed = FALSE;
1111 41220 : nPoints = 0;
1112 41220 : nMaxPoints = 0;
1113 41220 : padfX = NULL;
1114 41220 : padfY = NULL;
1115 :
1116 41220 : bLeftIsHigh = FALSE;
1117 :
1118 41220 : dfTailX = 0.0;
1119 41220 : }
1120 :
1121 : /************************************************************************/
1122 : /* ~GDALContourItem() */
1123 : /************************************************************************/
1124 :
1125 41220 : GDALContourItem::~GDALContourItem()
1126 :
1127 : {
1128 41220 : CPLFree( padfX );
1129 41220 : CPLFree( padfY );
1130 41220 : }
1131 :
1132 : /************************************************************************/
1133 : /* AddSegment() */
1134 : /************************************************************************/
1135 :
1136 41516 : int GDALContourItem::AddSegment( double dfXStart, double dfYStart,
1137 : double dfXEnd, double dfYEnd,
1138 : int bLeftHigh)
1139 :
1140 : {
1141 41516 : MakeRoomFor( nPoints + 1 );
1142 :
1143 : /* -------------------------------------------------------------------- */
1144 : /* If there are no segments, just add now. */
1145 : /* -------------------------------------------------------------------- */
1146 41516 : if( nPoints == 0 )
1147 : {
1148 41220 : nPoints = 2;
1149 :
1150 41220 : padfX[0] = dfXStart;
1151 41220 : padfY[0] = dfYStart;
1152 41220 : padfX[1] = dfXEnd;
1153 41220 : padfY[1] = dfYEnd;
1154 41220 : bRecentlyAccessed = TRUE;
1155 :
1156 41220 : dfTailX = padfX[1];
1157 :
1158 : // Here we know that the left of this vector is the high side
1159 41220 : bLeftIsHigh = bLeftHigh;
1160 :
1161 41220 : return TRUE;
1162 : }
1163 :
1164 : /* -------------------------------------------------------------------- */
1165 : /* Try to matching up with one of the ends, and insert. */
1166 : /* -------------------------------------------------------------------- */
1167 490 : if( fabs(padfX[nPoints-1]-dfXStart) < JOIN_DIST
1168 194 : && fabs(padfY[nPoints-1]-dfYStart) < JOIN_DIST )
1169 : {
1170 194 : padfX[nPoints] = dfXEnd;
1171 194 : padfY[nPoints] = dfYEnd;
1172 194 : nPoints++;
1173 :
1174 194 : bRecentlyAccessed = TRUE;
1175 :
1176 194 : dfTailX = dfXEnd;
1177 :
1178 194 : return TRUE;
1179 : }
1180 204 : else if( fabs(padfX[nPoints-1]-dfXEnd) < JOIN_DIST
1181 102 : && fabs(padfY[nPoints-1]-dfYEnd) < JOIN_DIST )
1182 : {
1183 102 : padfX[nPoints] = dfXStart;
1184 102 : padfY[nPoints] = dfYStart;
1185 102 : nPoints++;
1186 :
1187 102 : bRecentlyAccessed = TRUE;
1188 :
1189 102 : dfTailX = dfXStart;
1190 :
1191 102 : return TRUE;
1192 : }
1193 : else
1194 0 : return FALSE;
1195 : }
1196 :
1197 : /************************************************************************/
1198 : /* Merge() */
1199 : /************************************************************************/
1200 :
1201 174190 : int GDALContourItem::Merge( GDALContourItem *poOther )
1202 :
1203 : {
1204 174190 : if( poOther->dfLevel != dfLevel )
1205 0 : return FALSE;
1206 :
1207 : /* -------------------------------------------------------------------- */
1208 : /* Try to matching up with one of the ends, and insert. */
1209 : /* -------------------------------------------------------------------- */
1210 193362 : if( fabs(padfX[nPoints-1]-poOther->padfX[0]) < JOIN_DIST
1211 19172 : && fabs(padfY[nPoints-1]-poOther->padfY[0]) < JOIN_DIST )
1212 : {
1213 17734 : MakeRoomFor( nPoints + poOther->nPoints - 1 );
1214 :
1215 : memcpy( padfX + nPoints, poOther->padfX + 1,
1216 17734 : sizeof(double) * (poOther->nPoints-1) );
1217 : memcpy( padfY + nPoints, poOther->padfY + 1,
1218 17734 : sizeof(double) * (poOther->nPoints-1) );
1219 17734 : nPoints += poOther->nPoints - 1;
1220 :
1221 17734 : bRecentlyAccessed = TRUE;
1222 :
1223 17734 : dfTailX = padfX[nPoints-1];
1224 :
1225 17734 : return TRUE;
1226 : }
1227 167204 : else if( fabs(padfX[0]-poOther->padfX[poOther->nPoints-1]) < JOIN_DIST
1228 10748 : && fabs(padfY[0]-poOther->padfY[poOther->nPoints-1]) < JOIN_DIST )
1229 : {
1230 10070 : MakeRoomFor( nPoints + poOther->nPoints - 1 );
1231 :
1232 : memmove( padfX + poOther->nPoints - 1, padfX,
1233 10070 : sizeof(double) * nPoints );
1234 : memmove( padfY + poOther->nPoints - 1, padfY,
1235 10070 : sizeof(double) * nPoints );
1236 : memcpy( padfX, poOther->padfX,
1237 10070 : sizeof(double) * (poOther->nPoints-1) );
1238 : memcpy( padfY, poOther->padfY,
1239 10070 : sizeof(double) * (poOther->nPoints-1) );
1240 10070 : nPoints += poOther->nPoints - 1;
1241 :
1242 10070 : bRecentlyAccessed = TRUE;
1243 :
1244 10070 : dfTailX = padfX[nPoints-1];
1245 :
1246 10070 : return TRUE;
1247 : }
1248 154234 : else if( fabs(padfX[nPoints-1]-poOther->padfX[poOther->nPoints-1]) < JOIN_DIST
1249 7848 : && fabs(padfY[nPoints-1]-poOther->padfY[poOther->nPoints-1]) < JOIN_DIST )
1250 : {
1251 : int i;
1252 :
1253 7214 : MakeRoomFor( nPoints + poOther->nPoints - 1 );
1254 :
1255 38890 : for( i = 0; i < poOther->nPoints-1; i++ )
1256 : {
1257 31676 : padfX[i+nPoints] = poOther->padfX[poOther->nPoints-i-2];
1258 31676 : padfY[i+nPoints] = poOther->padfY[poOther->nPoints-i-2];
1259 : }
1260 :
1261 7214 : nPoints += poOther->nPoints - 1;
1262 :
1263 7214 : bRecentlyAccessed = TRUE;
1264 :
1265 7214 : dfTailX = padfX[nPoints-1];
1266 :
1267 7214 : return TRUE;
1268 : }
1269 144438 : else if( fabs(padfX[0]-poOther->padfX[0]) < JOIN_DIST
1270 5266 : && fabs(padfY[0]-poOther->padfY[0]) < JOIN_DIST )
1271 : {
1272 : int i;
1273 :
1274 4688 : MakeRoomFor( nPoints + poOther->nPoints - 1 );
1275 :
1276 : memmove( padfX + poOther->nPoints - 1, padfX,
1277 4688 : sizeof(double) * nPoints );
1278 : memmove( padfY + poOther->nPoints - 1, padfY,
1279 4688 : sizeof(double) * nPoints );
1280 :
1281 34380 : for( i = 0; i < poOther->nPoints-1; i++ )
1282 : {
1283 29692 : padfX[i] = poOther->padfX[poOther->nPoints - i - 1];
1284 29692 : padfY[i] = poOther->padfY[poOther->nPoints - i - 1];
1285 : }
1286 :
1287 4688 : nPoints += poOther->nPoints - 1;
1288 :
1289 4688 : bRecentlyAccessed = TRUE;
1290 :
1291 4688 : dfTailX = padfX[nPoints-1];
1292 :
1293 4688 : return TRUE;
1294 : }
1295 : else
1296 134484 : return FALSE;
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* MakeRoomFor() */
1301 : /************************************************************************/
1302 :
1303 81222 : void GDALContourItem::MakeRoomFor( int nNewPoints )
1304 :
1305 : {
1306 81222 : if( nNewPoints > nMaxPoints )
1307 : {
1308 46446 : nMaxPoints = nNewPoints * 2 + 50;
1309 46446 : padfX = (double *) CPLRealloc(padfX,sizeof(double) * nMaxPoints);
1310 46446 : padfY = (double *) CPLRealloc(padfY,sizeof(double) * nMaxPoints);
1311 : }
1312 81222 : }
1313 :
1314 : /************************************************************************/
1315 : /* PrepareEjection() */
1316 : /************************************************************************/
1317 :
1318 1514 : void GDALContourItem::PrepareEjection()
1319 :
1320 : {
1321 : /* If left side is the high side, then reverse to get curve normal
1322 : ** pointing downwards
1323 : */
1324 1514 : if( bLeftIsHigh )
1325 : {
1326 : int i;
1327 :
1328 : // Reverse the arrays
1329 16176 : for( i = 0; i < nPoints / 2; i++ )
1330 : {
1331 : double dfTemp;
1332 15140 : dfTemp = padfX[i];
1333 15140 : padfX[i] = padfX[ nPoints - i - 1];
1334 15140 : padfX[ nPoints - i - 1] = dfTemp;
1335 :
1336 15140 : dfTemp = padfY[i];
1337 15140 : padfY[i] = padfY[ nPoints - i - 1];
1338 15140 : padfY[ nPoints - i - 1] = dfTemp;
1339 : }
1340 : }
1341 1514 : }
1342 :
1343 :
1344 : /************************************************************************/
1345 : /* ==================================================================== */
1346 : /* Additional C Callable Functions */
1347 : /* ==================================================================== */
1348 : /************************************************************************/
1349 :
1350 : /************************************************************************/
1351 : /* OGRContourWriter() */
1352 : /************************************************************************/
1353 :
1354 1514 : CPLErr OGRContourWriter( double dfLevel,
1355 : int nPoints, double *padfX, double *padfY,
1356 : void *pInfo )
1357 :
1358 : {
1359 1514 : OGRContourWriterInfo *poInfo = (OGRContourWriterInfo *) pInfo;
1360 : OGRFeatureH hFeat;
1361 : OGRGeometryH hGeom;
1362 : int iPoint;
1363 :
1364 1514 : hFeat = OGR_F_Create( OGR_L_GetLayerDefn( (OGRLayerH) poInfo->hLayer ) );
1365 :
1366 1514 : if( poInfo->nIDField != -1 )
1367 1514 : OGR_F_SetFieldInteger( hFeat, poInfo->nIDField, poInfo->nNextID++ );
1368 :
1369 1514 : if( poInfo->nElevField != -1 )
1370 278 : OGR_F_SetFieldDouble( hFeat, poInfo->nElevField, dfLevel );
1371 :
1372 1514 : hGeom = OGR_G_CreateGeometry( wkbLineString );
1373 :
1374 44544 : for( iPoint = nPoints-1; iPoint >= 0; iPoint-- )
1375 : {
1376 : OGR_G_SetPoint( hGeom, iPoint,
1377 43030 : poInfo->adfGeoTransform[0]
1378 43030 : + poInfo->adfGeoTransform[1] * padfX[iPoint]
1379 43030 : + poInfo->adfGeoTransform[2] * padfY[iPoint],
1380 43030 : poInfo->adfGeoTransform[3]
1381 43030 : + poInfo->adfGeoTransform[4] * padfX[iPoint]
1382 43030 : + poInfo->adfGeoTransform[5] * padfY[iPoint],
1383 172120 : dfLevel );
1384 : }
1385 :
1386 1514 : OGR_F_SetGeometryDirectly( hFeat, hGeom );
1387 :
1388 1514 : OGR_L_CreateFeature( (OGRLayerH) poInfo->hLayer, hFeat );
1389 1514 : OGR_F_Destroy( hFeat );
1390 :
1391 1514 : return CE_None;
1392 : }
1393 : #endif // OGR_ENABLED
1394 :
1395 : /************************************************************************/
1396 : /* GDALContourGenerate() */
1397 : /************************************************************************/
1398 :
1399 : /**
1400 : * Create vector contours from raster DEM.
1401 : *
1402 : * This algorithm will generate contour vectors for the input raster band
1403 : * on the requested set of contour levels. The vector contours are written
1404 : * to the passed in OGR vector layer. Also, a NODATA value may be specified
1405 : * to identify pixels that should not be considered in contour line generation.
1406 : *
1407 : * The gdal/apps/gdal_contour.cpp mainline can be used as an example of
1408 : * how to use this function.
1409 : *
1410 : * ALGORITHM RULES
1411 :
1412 : For contouring purposes raster pixel values are assumed to represent a point
1413 : value at the center of the corresponding pixel region. For the purpose of
1414 : contour generation we virtually connect each pixel center to the values to
1415 : the left, right, top and bottom. We assume that the pixel value is linearly
1416 : interpolated between the pixel centers along each line, and determine where
1417 : (if any) contour lines will appear along these line segements. Then the
1418 : contour crossings are connected.
1419 :
1420 : This means that contour lines' nodes won't actually be on pixel edges, but
1421 : rather along vertical and horizontal lines connecting the pixel centers.
1422 :
1423 : \verbatim
1424 : General Case:
1425 :
1426 : 5 | | 3
1427 : -- + ---------------- + --
1428 : | |
1429 : | |
1430 : | |
1431 : | |
1432 : 10 + |
1433 : |\ |
1434 : | \ |
1435 : -- + -+-------------- + --
1436 : 12 | 10 | 1
1437 :
1438 :
1439 : Saddle Point:
1440 :
1441 : 5 | | 12
1442 : -- + -------------+-- + --
1443 : | \ |
1444 : | \|
1445 : | +
1446 : | |
1447 : + |
1448 : |\ |
1449 : | \ |
1450 : -- + -+-------------- + --
1451 : 12 | | 1
1452 :
1453 : or:
1454 :
1455 : 5 | | 12
1456 : -- + -------------+-- + --
1457 : | __/ |
1458 : | ___/ |
1459 : | ___/ __+
1460 : | / __/ |
1461 : +' __/ |
1462 : | __/ |
1463 : | ,__/ |
1464 : -- + -+-------------- + --
1465 : 12 | | 1
1466 : \endverbatim
1467 :
1468 : Nodata:
1469 :
1470 : In the "nodata" case we treat the whole nodata pixel as a no-mans land.
1471 : We extend the corner pixels near the nodata out to half way and then
1472 : construct extra lines from those points to the center which is assigned
1473 : an averaged value from the two nearby points (in this case (12+3+5)/3).
1474 :
1475 : \verbatim
1476 : 5 | | 3
1477 : -- + ---------------- + --
1478 : | |
1479 : | |
1480 : | 6.7 |
1481 : | +---------+ 3
1482 : 10 +___ |
1483 : | \____+ 10
1484 : | |
1485 : -- + -------+ +
1486 : 12 | 12 (nodata)
1487 :
1488 : \endverbatim
1489 :
1490 : *
1491 : * @param hBand The band to read raster data from. The whole band will be
1492 : * processed.
1493 : *
1494 : * @param dfContourInterval The elevation interval between contours generated.
1495 : *
1496 : * @param dfContourBase The "base" relative to which contour intervals are
1497 : * applied. This is normally zero, but could be different. To generate 10m
1498 : * contours at 5, 15, 25, ... the ContourBase would be 5.
1499 : *
1500 : * @param nFixedLevelCount The number of fixed levels. If this is greater than
1501 : * zero, then fixed levels will be used, and ContourInterval and ContourBase
1502 : * are ignored.
1503 : *
1504 : * @param padfFixedLevels The list of fixed contour levels at which contours
1505 : * should be generated. It will contain FixedLevelCount entries, and may be
1506 : * NULL if fixed levels are disabled (FixedLevelCount = 0).
1507 : *
1508 : * @param bUseNoData If TRUE the dfNoDataValue will be used.
1509 : *
1510 : * @param dfNoDataValue The value to use as a "nodata" value. That is, a
1511 : * pixel value which should be ignored in generating contours as if the value
1512 : * of the pixel were not known.
1513 : *
1514 : * @param hLayer The layer to which new contour vectors will be written.
1515 : * Each contour will have a LINESTRING geometry attached to it. This
1516 : * is really of type OGRLayerH, but void * is used to avoid pulling the
1517 : * ogr_api.h file in here.
1518 : *
1519 : * @param iIDField If not -1 this will be used as a field index to indicate
1520 : * where a unique id should be written for each feature (contour) written.
1521 : *
1522 : * @param iElevField If not -1 this will be used as a field index to indicate
1523 : * where the elevation value of the contour should be written.
1524 : *
1525 : * @param pfnProgress A GDALProgressFunc that may be used to report progress
1526 : * to the user, or to interrupt the algorithm. May be NULL if not required.
1527 : *
1528 : * @param pProgressArg The callback data for the pfnProgress function.
1529 : *
1530 : * @return CE_None on success or CE_Failure if an error occurs.
1531 : */
1532 :
1533 16 : CPLErr GDALContourGenerate( GDALRasterBandH hBand,
1534 : double dfContourInterval, double dfContourBase,
1535 : int nFixedLevelCount, double *padfFixedLevels,
1536 : int bUseNoData, double dfNoDataValue,
1537 : void *hLayer, int iIDField, int iElevField,
1538 : GDALProgressFunc pfnProgress, void *pProgressArg )
1539 :
1540 : {
1541 : #ifndef OGR_ENABLED
1542 : CPLError(CE_Failure, CPLE_NotSupported, "GDALContourGenerate() unimplemented in a non OGR build");
1543 : return CE_Failure;
1544 : #else
1545 16 : VALIDATE_POINTER1( hBand, "GDALContourGenerate", CE_Failure );
1546 :
1547 : OGRContourWriterInfo oCWI;
1548 :
1549 16 : if( pfnProgress == NULL )
1550 4 : pfnProgress = GDALDummyProgress;
1551 :
1552 16 : if( !pfnProgress( 0.0, "", pProgressArg ) )
1553 : {
1554 0 : CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
1555 0 : return CE_Failure;
1556 : }
1557 :
1558 : /* -------------------------------------------------------------------- */
1559 : /* Setup contour writer information. */
1560 : /* -------------------------------------------------------------------- */
1561 : GDALDatasetH hSrcDS;
1562 :
1563 16 : oCWI.hLayer = (OGRLayerH) hLayer;
1564 :
1565 16 : oCWI.nElevField = iElevField;
1566 16 : oCWI.nIDField = iIDField;
1567 :
1568 16 : hSrcDS = GDALGetBandDataset( hBand );
1569 16 : GDALGetGeoTransform( hSrcDS, oCWI.adfGeoTransform );
1570 16 : oCWI.nNextID = 0;
1571 :
1572 : /* -------------------------------------------------------------------- */
1573 : /* Setup contour generator. */
1574 : /* -------------------------------------------------------------------- */
1575 16 : int nXSize = GDALGetRasterBandXSize( hBand );
1576 16 : int nYSize = GDALGetRasterBandYSize( hBand );
1577 :
1578 16 : GDALContourGenerator oCG( nXSize, nYSize, OGRContourWriter, &oCWI );
1579 :
1580 16 : if( nFixedLevelCount > 0 )
1581 4 : oCG.SetFixedLevels( nFixedLevelCount, padfFixedLevels );
1582 : else
1583 12 : oCG.SetContourLevels( dfContourInterval, dfContourBase );
1584 :
1585 16 : if( bUseNoData )
1586 6 : oCG.SetNoData( dfNoDataValue );
1587 :
1588 : /* -------------------------------------------------------------------- */
1589 : /* Feed the data into the contour generator. */
1590 : /* -------------------------------------------------------------------- */
1591 : int iLine;
1592 : double *padfScanline;
1593 16 : CPLErr eErr = CE_None;
1594 :
1595 16 : padfScanline = (double *) VSIMalloc(sizeof(double) * nXSize);
1596 16 : if (padfScanline == NULL)
1597 : {
1598 : CPLError( CE_Failure, CPLE_OutOfMemory,
1599 0 : "VSIMalloc(): Out of memory in GDALContourGenerate" );
1600 0 : return CE_Failure;
1601 : }
1602 :
1603 2110 : for( iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
1604 : {
1605 : GDALRasterIO( hBand, GF_Read, 0, iLine, nXSize, 1,
1606 2094 : padfScanline, nXSize, 1, GDT_Float64, 0, 0 );
1607 2094 : eErr = oCG.FeedLine( padfScanline );
1608 :
1609 2094 : if( eErr == CE_None
1610 : && !pfnProgress( (iLine+1) / (double) nYSize, "", pProgressArg ) )
1611 : {
1612 0 : CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
1613 0 : eErr = CE_Failure;
1614 : }
1615 : }
1616 :
1617 16 : CPLFree( padfScanline );
1618 :
1619 16 : return eErr;
1620 : #endif // OGR_ENABLED
1621 : }
|