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