/src/gdal/alg/gdalproximity.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: Compute each pixel's proximity to a set of target pixels. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2008, Frank Warmerdam |
9 | | * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "gdal_alg.h" |
16 | | |
17 | | #include <cmath> |
18 | | #include <cstdlib> |
19 | | |
20 | | #include <algorithm> |
21 | | |
22 | | #include "cpl_conv.h" |
23 | | #include "cpl_error.h" |
24 | | #include "cpl_progress.h" |
25 | | #include "cpl_string.h" |
26 | | #include "cpl_vsi.h" |
27 | | #include "gdal.h" |
28 | | |
29 | | static CPLErr ProcessProximityLine(GInt32 *panSrcScanline, int *panNearX, |
30 | | int *panNearY, int bForward, int iLine, |
31 | | int nXSize, double nMaxDist, |
32 | | float *pafProximity, |
33 | | double *pdfSrcNoDataValue, int nTargetValues, |
34 | | int *panTargetValues); |
35 | | |
36 | | /************************************************************************/ |
37 | | /* GDALComputeProximity() */ |
38 | | /************************************************************************/ |
39 | | |
40 | | /** |
41 | | Compute the proximity of all pixels in the image to a set of pixels in |
42 | | the source image. |
43 | | |
44 | | This function attempts to compute the proximity of all pixels in |
45 | | the image to a set of pixels in the source image. The following |
46 | | options are used to define the behavior of the function. By |
47 | | default all non-zero pixels in hSrcBand will be considered the |
48 | | "target", and all proximities will be computed in pixels. Note |
49 | | that target pixels are set to the value corresponding to a distance |
50 | | of zero. |
51 | | |
52 | | The progress function args may be NULL or a valid progress reporting function |
53 | | such as GDALTermProgress/NULL. |
54 | | |
55 | | Options: |
56 | | |
57 | | VALUES=n[,n]* |
58 | | |
59 | | A list of target pixel values to measure the distance from. If this |
60 | | option is not provided proximity will be computed from non-zero |
61 | | pixel values. Currently pixel values are internally processed as |
62 | | integers. |
63 | | |
64 | | DISTUNITS=[PIXEL]/GEO |
65 | | |
66 | | Indicates whether distances will be computed in pixel units or |
67 | | in georeferenced units. The default is pixel units. This also |
68 | | determines the interpretation of MAXDIST. |
69 | | |
70 | | MAXDIST=n |
71 | | |
72 | | The maximum distance to search. Proximity distances greater than |
73 | | this value will not be computed. Instead output pixels will be |
74 | | set to a nodata value. |
75 | | |
76 | | NODATA=n |
77 | | |
78 | | The NODATA value to use on the output band for pixels that are |
79 | | beyond MAXDIST. If not provided, the hProximityBand will be |
80 | | queried for a nodata value. If one is not found, 65535 will be used. |
81 | | |
82 | | USE_INPUT_NODATA=YES/NO |
83 | | |
84 | | If this option is set, the input data set no-data value will be |
85 | | respected. Leaving no data pixels in the input as no data pixels in |
86 | | the proximity output. |
87 | | |
88 | | FIXED_BUF_VAL=n |
89 | | |
90 | | If this option is set, all pixels within the MAXDIST threshold are |
91 | | set to this fixed value instead of to a proximity distance. |
92 | | */ |
93 | | |
94 | | CPLErr CPL_STDCALL GDALComputeProximity(GDALRasterBandH hSrcBand, |
95 | | GDALRasterBandH hProximityBand, |
96 | | char **papszOptions, |
97 | | GDALProgressFunc pfnProgress, |
98 | | void *pProgressArg) |
99 | | |
100 | 0 | { |
101 | 0 | VALIDATE_POINTER1(hSrcBand, "GDALComputeProximity", CE_Failure); |
102 | 0 | VALIDATE_POINTER1(hProximityBand, "GDALComputeProximity", CE_Failure); |
103 | | |
104 | 0 | if (pfnProgress == nullptr) |
105 | 0 | pfnProgress = GDALDummyProgress; |
106 | | |
107 | | /* -------------------------------------------------------------------- */ |
108 | | /* Are we using pixels or georeferenced coordinates for distances? */ |
109 | | /* -------------------------------------------------------------------- */ |
110 | 0 | double dfDistMult = 1.0; |
111 | 0 | const char *pszOpt = CSLFetchNameValue(papszOptions, "DISTUNITS"); |
112 | 0 | if (pszOpt) |
113 | 0 | { |
114 | 0 | if (EQUAL(pszOpt, "GEO")) |
115 | 0 | { |
116 | 0 | GDALDatasetH hSrcDS = GDALGetBandDataset(hSrcBand); |
117 | 0 | if (hSrcDS) |
118 | 0 | { |
119 | 0 | double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; |
120 | |
|
121 | 0 | GDALGetGeoTransform(hSrcDS, adfGeoTransform); |
122 | 0 | if (std::abs(adfGeoTransform[1]) != |
123 | 0 | std::abs(adfGeoTransform[5])) |
124 | 0 | CPLError( |
125 | 0 | CE_Warning, CPLE_AppDefined, |
126 | 0 | "Pixels not square, distances will be inaccurate."); |
127 | 0 | dfDistMult = std::abs(adfGeoTransform[1]); |
128 | 0 | } |
129 | 0 | } |
130 | 0 | else if (!EQUAL(pszOpt, "PIXEL")) |
131 | 0 | { |
132 | 0 | CPLError( |
133 | 0 | CE_Failure, CPLE_AppDefined, |
134 | 0 | "Unrecognized DISTUNITS value '%s', should be GEO or PIXEL.", |
135 | 0 | pszOpt); |
136 | 0 | return CE_Failure; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | /* -------------------------------------------------------------------- */ |
141 | | /* What is our maxdist value? */ |
142 | | /* -------------------------------------------------------------------- */ |
143 | 0 | pszOpt = CSLFetchNameValue(papszOptions, "MAXDIST"); |
144 | 0 | const double dfMaxDist = pszOpt ? CPLAtof(pszOpt) / dfDistMult |
145 | 0 | : GDALGetRasterBandXSize(hSrcBand) + |
146 | 0 | GDALGetRasterBandYSize(hSrcBand); |
147 | |
|
148 | 0 | CPLDebug("GDAL", "MAXDIST=%g, DISTMULT=%g", dfMaxDist, dfDistMult); |
149 | | |
150 | | /* -------------------------------------------------------------------- */ |
151 | | /* Verify the source and destination are compatible. */ |
152 | | /* -------------------------------------------------------------------- */ |
153 | 0 | const int nXSize = GDALGetRasterBandXSize(hSrcBand); |
154 | 0 | const int nYSize = GDALGetRasterBandYSize(hSrcBand); |
155 | 0 | if (nXSize != GDALGetRasterBandXSize(hProximityBand) || |
156 | 0 | nYSize != GDALGetRasterBandYSize(hProximityBand)) |
157 | 0 | { |
158 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
159 | 0 | "Source and proximity bands are not the same size."); |
160 | 0 | return CE_Failure; |
161 | 0 | } |
162 | | |
163 | | /* -------------------------------------------------------------------- */ |
164 | | /* Get input NODATA value. */ |
165 | | /* -------------------------------------------------------------------- */ |
166 | 0 | double dfSrcNoDataValue = 0.0; |
167 | 0 | double *pdfSrcNoData = nullptr; |
168 | 0 | if (CPLFetchBool(papszOptions, "USE_INPUT_NODATA", false)) |
169 | 0 | { |
170 | 0 | int bSrcHasNoData = 0; |
171 | 0 | dfSrcNoDataValue = GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData); |
172 | 0 | if (bSrcHasNoData) |
173 | 0 | pdfSrcNoData = &dfSrcNoDataValue; |
174 | 0 | } |
175 | | |
176 | | /* -------------------------------------------------------------------- */ |
177 | | /* Get output NODATA value. */ |
178 | | /* -------------------------------------------------------------------- */ |
179 | 0 | float fNoDataValue = 0.0f; |
180 | 0 | pszOpt = CSLFetchNameValue(papszOptions, "NODATA"); |
181 | 0 | if (pszOpt != nullptr) |
182 | 0 | { |
183 | 0 | fNoDataValue = static_cast<float>(CPLAtof(pszOpt)); |
184 | 0 | } |
185 | 0 | else |
186 | 0 | { |
187 | 0 | int bSuccess = FALSE; |
188 | |
|
189 | 0 | fNoDataValue = static_cast<float>( |
190 | 0 | GDALGetRasterNoDataValue(hProximityBand, &bSuccess)); |
191 | 0 | if (!bSuccess) |
192 | 0 | fNoDataValue = 65535.0; |
193 | 0 | } |
194 | | |
195 | | /* -------------------------------------------------------------------- */ |
196 | | /* Is there a fixed value we wish to force the buffer area to? */ |
197 | | /* -------------------------------------------------------------------- */ |
198 | 0 | double dfFixedBufVal = 0.0; |
199 | 0 | bool bFixedBufVal = false; |
200 | 0 | pszOpt = CSLFetchNameValue(papszOptions, "FIXED_BUF_VAL"); |
201 | 0 | if (pszOpt) |
202 | 0 | { |
203 | 0 | dfFixedBufVal = CPLAtof(pszOpt); |
204 | 0 | bFixedBufVal = true; |
205 | 0 | } |
206 | | |
207 | | /* -------------------------------------------------------------------- */ |
208 | | /* Get the target value(s). */ |
209 | | /* -------------------------------------------------------------------- */ |
210 | 0 | int *panTargetValues = nullptr; |
211 | 0 | int nTargetValues = 0; |
212 | |
|
213 | 0 | pszOpt = CSLFetchNameValue(papszOptions, "VALUES"); |
214 | 0 | if (pszOpt != nullptr) |
215 | 0 | { |
216 | 0 | char **papszValuesTokens = |
217 | 0 | CSLTokenizeStringComplex(pszOpt, ",", FALSE, FALSE); |
218 | |
|
219 | 0 | nTargetValues = CSLCount(papszValuesTokens); |
220 | 0 | panTargetValues = |
221 | 0 | static_cast<int *>(CPLCalloc(sizeof(int), nTargetValues)); |
222 | |
|
223 | 0 | for (int i = 0; i < nTargetValues; i++) |
224 | 0 | panTargetValues[i] = atoi(papszValuesTokens[i]); |
225 | 0 | CSLDestroy(papszValuesTokens); |
226 | 0 | } |
227 | | |
228 | | /* -------------------------------------------------------------------- */ |
229 | | /* Initialize progress counter. */ |
230 | | /* -------------------------------------------------------------------- */ |
231 | 0 | if (!pfnProgress(0.0, "", pProgressArg)) |
232 | 0 | { |
233 | 0 | CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); |
234 | 0 | CPLFree(panTargetValues); |
235 | 0 | return CE_Failure; |
236 | 0 | } |
237 | | |
238 | | /* -------------------------------------------------------------------- */ |
239 | | /* We need a signed type for the working proximity values kept */ |
240 | | /* on disk. If our proximity band is not signed, then create a */ |
241 | | /* temporary file for this purpose. */ |
242 | | /* -------------------------------------------------------------------- */ |
243 | 0 | GDALRasterBandH hWorkProximityBand = hProximityBand; |
244 | 0 | GDALDatasetH hWorkProximityDS = nullptr; |
245 | 0 | const GDALDataType eProxType = GDALGetRasterDataType(hProximityBand); |
246 | 0 | CPLErr eErr = CE_None; |
247 | | |
248 | | // TODO(schwehr): Localize after removing gotos. |
249 | 0 | float *pafProximity = nullptr; |
250 | 0 | int *panNearX = nullptr; |
251 | 0 | int *panNearY = nullptr; |
252 | 0 | GInt32 *panSrcScanline = nullptr; |
253 | 0 | bool bTempFileAlreadyDeleted = false; |
254 | |
|
255 | 0 | if (eProxType == GDT_UInt8 || eProxType == GDT_UInt16 || |
256 | 0 | eProxType == GDT_UInt32) |
257 | 0 | { |
258 | 0 | GDALDriverH hDriver = GDALGetDriverByName("GTiff"); |
259 | 0 | if (hDriver == nullptr) |
260 | 0 | { |
261 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
262 | 0 | "GDALComputeProximity needs GTiff driver"); |
263 | 0 | eErr = CE_Failure; |
264 | 0 | goto end; |
265 | 0 | } |
266 | 0 | CPLString osTmpFile = CPLGenerateTempFilenameSafe("proximity"); |
267 | 0 | hWorkProximityDS = GDALCreate(hDriver, osTmpFile, nXSize, nYSize, 1, |
268 | 0 | GDT_Float32, nullptr); |
269 | 0 | if (hWorkProximityDS == nullptr) |
270 | 0 | { |
271 | 0 | eErr = CE_Failure; |
272 | 0 | goto end; |
273 | 0 | } |
274 | | // On Unix, attempt at deleting the temporary file now, so that |
275 | | // if the process gets interrupted, it is automatically destroyed |
276 | | // by the operating system. |
277 | 0 | bTempFileAlreadyDeleted = VSIUnlink(osTmpFile) == 0; |
278 | 0 | hWorkProximityBand = GDALGetRasterBand(hWorkProximityDS, 1); |
279 | 0 | } |
280 | | |
281 | | /* -------------------------------------------------------------------- */ |
282 | | /* Allocate buffer for two scanlines of distances as floats */ |
283 | | /* (the current and last line). */ |
284 | | /* -------------------------------------------------------------------- */ |
285 | 0 | pafProximity = |
286 | 0 | static_cast<float *>(VSI_MALLOC2_VERBOSE(sizeof(float), nXSize)); |
287 | 0 | panNearX = static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nXSize)); |
288 | 0 | panNearY = static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nXSize)); |
289 | 0 | panSrcScanline = |
290 | 0 | static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize)); |
291 | |
|
292 | 0 | if (pafProximity == nullptr || panNearX == nullptr || panNearY == nullptr || |
293 | 0 | panSrcScanline == nullptr) |
294 | 0 | { |
295 | 0 | eErr = CE_Failure; |
296 | 0 | goto end; |
297 | 0 | } |
298 | | |
299 | | /* -------------------------------------------------------------------- */ |
300 | | /* Loop from top to bottom of the image. */ |
301 | | /* -------------------------------------------------------------------- */ |
302 | | |
303 | 0 | for (int i = 0; i < nXSize; i++) |
304 | 0 | { |
305 | 0 | panNearX[i] = -1; |
306 | 0 | panNearY[i] = -1; |
307 | 0 | } |
308 | |
|
309 | 0 | for (int iLine = 0; eErr == CE_None && iLine < nYSize; iLine++) |
310 | 0 | { |
311 | | // Read for target values. |
312 | 0 | eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iLine, nXSize, 1, |
313 | 0 | panSrcScanline, nXSize, 1, GDT_Int32, 0, 0); |
314 | 0 | if (eErr != CE_None) |
315 | 0 | break; |
316 | | |
317 | 0 | for (int i = 0; i < nXSize; i++) |
318 | 0 | pafProximity[i] = -1.0; |
319 | | |
320 | | // Left to right. |
321 | 0 | ProcessProximityLine(panSrcScanline, panNearX, panNearY, TRUE, iLine, |
322 | 0 | nXSize, dfMaxDist, pafProximity, pdfSrcNoData, |
323 | 0 | nTargetValues, panTargetValues); |
324 | | |
325 | | // Right to Left. |
326 | 0 | ProcessProximityLine(panSrcScanline, panNearX, panNearY, FALSE, iLine, |
327 | 0 | nXSize, dfMaxDist, pafProximity, pdfSrcNoData, |
328 | 0 | nTargetValues, panTargetValues); |
329 | | |
330 | | // Write out results. |
331 | 0 | eErr = GDALRasterIO(hWorkProximityBand, GF_Write, 0, iLine, nXSize, 1, |
332 | 0 | pafProximity, nXSize, 1, GDT_Float32, 0, 0); |
333 | |
|
334 | 0 | if (eErr != CE_None) |
335 | 0 | break; |
336 | | |
337 | 0 | if (!pfnProgress(0.5 * (iLine + 1) / static_cast<double>(nYSize), "", |
338 | 0 | pProgressArg)) |
339 | 0 | { |
340 | 0 | CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); |
341 | 0 | eErr = CE_Failure; |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | /* -------------------------------------------------------------------- */ |
346 | | /* Loop from bottom to top of the image. */ |
347 | | /* -------------------------------------------------------------------- */ |
348 | 0 | for (int i = 0; i < nXSize; i++) |
349 | 0 | { |
350 | 0 | panNearX[i] = -1; |
351 | 0 | panNearY[i] = -1; |
352 | 0 | } |
353 | |
|
354 | 0 | for (int iLine = nYSize - 1; eErr == CE_None && iLine >= 0; iLine--) |
355 | 0 | { |
356 | | // Read first pass proximity. |
357 | 0 | eErr = GDALRasterIO(hWorkProximityBand, GF_Read, 0, iLine, nXSize, 1, |
358 | 0 | pafProximity, nXSize, 1, GDT_Float32, 0, 0); |
359 | |
|
360 | 0 | if (eErr != CE_None) |
361 | 0 | break; |
362 | | |
363 | | // Read pixel values. |
364 | | |
365 | 0 | eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iLine, nXSize, 1, |
366 | 0 | panSrcScanline, nXSize, 1, GDT_Int32, 0, 0); |
367 | 0 | if (eErr != CE_None) |
368 | 0 | break; |
369 | | |
370 | | // Right to left. |
371 | 0 | ProcessProximityLine(panSrcScanline, panNearX, panNearY, FALSE, iLine, |
372 | 0 | nXSize, dfMaxDist, pafProximity, pdfSrcNoData, |
373 | 0 | nTargetValues, panTargetValues); |
374 | | |
375 | | // Left to right. |
376 | 0 | ProcessProximityLine(panSrcScanline, panNearX, panNearY, TRUE, iLine, |
377 | 0 | nXSize, dfMaxDist, pafProximity, pdfSrcNoData, |
378 | 0 | nTargetValues, panTargetValues); |
379 | | |
380 | | // Final post processing of distances. |
381 | 0 | for (int i = 0; i < nXSize; i++) |
382 | 0 | { |
383 | 0 | if (pafProximity[i] < 0.0f) |
384 | 0 | pafProximity[i] = fNoDataValue; |
385 | 0 | else if (pafProximity[i] > 0.0f) |
386 | 0 | { |
387 | 0 | if (bFixedBufVal) |
388 | 0 | pafProximity[i] = static_cast<float>(dfFixedBufVal); |
389 | 0 | else |
390 | 0 | pafProximity[i] = |
391 | 0 | pafProximity[i] * static_cast<float>(dfDistMult); |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | | // Write out results. |
396 | 0 | eErr = GDALRasterIO(hProximityBand, GF_Write, 0, iLine, nXSize, 1, |
397 | 0 | pafProximity, nXSize, 1, GDT_Float32, 0, 0); |
398 | |
|
399 | 0 | if (eErr != CE_None) |
400 | 0 | break; |
401 | | |
402 | 0 | if (!pfnProgress(0.5 + 0.5 * (nYSize - iLine) / |
403 | 0 | static_cast<double>(nYSize), |
404 | 0 | "", pProgressArg)) |
405 | 0 | { |
406 | 0 | CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); |
407 | 0 | eErr = CE_Failure; |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | /* -------------------------------------------------------------------- */ |
412 | | /* Cleanup */ |
413 | | /* -------------------------------------------------------------------- */ |
414 | 0 | end: |
415 | 0 | CPLFree(panNearX); |
416 | 0 | CPLFree(panNearY); |
417 | 0 | CPLFree(panSrcScanline); |
418 | 0 | CPLFree(pafProximity); |
419 | 0 | CPLFree(panTargetValues); |
420 | |
|
421 | 0 | if (hWorkProximityDS != nullptr) |
422 | 0 | { |
423 | 0 | CPLString osProxFile = GDALGetDescription(hWorkProximityDS); |
424 | 0 | GDALClose(hWorkProximityDS); |
425 | 0 | if (!bTempFileAlreadyDeleted) |
426 | 0 | { |
427 | 0 | GDALDeleteDataset(GDALGetDriverByName("GTiff"), osProxFile); |
428 | 0 | } |
429 | 0 | } |
430 | |
|
431 | 0 | return eErr; |
432 | 0 | } |
433 | | |
434 | | /************************************************************************/ |
435 | | /* SquareDistance() */ |
436 | | /************************************************************************/ |
437 | | |
438 | | static double SquareDistance(double dfX1, double dfX2, double dfY1, double dfY2) |
439 | 0 | { |
440 | 0 | const double dfDX = dfX1 - dfX2; |
441 | 0 | const double dfDY = dfY1 - dfY2; |
442 | 0 | return dfDX * dfDX + dfDY * dfDY; |
443 | 0 | } |
444 | | |
445 | | /************************************************************************/ |
446 | | /* ProcessProximityLine() */ |
447 | | /************************************************************************/ |
448 | | |
449 | | static CPLErr ProcessProximityLine(GInt32 *panSrcScanline, int *panNearX, |
450 | | int *panNearY, int bForward, int iLine, |
451 | | int nXSize, double dfMaxDist, |
452 | | float *pafProximity, |
453 | | double *pdfSrcNoDataValue, int nTargetValues, |
454 | | int *panTargetValues) |
455 | | |
456 | 0 | { |
457 | 0 | const int iStart = bForward ? 0 : nXSize - 1; |
458 | 0 | const int iEnd = bForward ? nXSize : -1; |
459 | 0 | const int iStep = bForward ? 1 : -1; |
460 | |
|
461 | 0 | for (int iPixel = iStart; iPixel != iEnd; iPixel += iStep) |
462 | 0 | { |
463 | 0 | bool bIsTarget = false; |
464 | | |
465 | | /* -------------------------------------------------------------------- |
466 | | */ |
467 | | /* Is the current pixel a target pixel? */ |
468 | | /* -------------------------------------------------------------------- |
469 | | */ |
470 | 0 | if (nTargetValues == 0) |
471 | 0 | { |
472 | 0 | bIsTarget = panSrcScanline[iPixel] != 0; |
473 | 0 | } |
474 | 0 | else |
475 | 0 | { |
476 | 0 | for (int i = 0; i < nTargetValues; i++) |
477 | 0 | { |
478 | 0 | if (panSrcScanline[iPixel] == panTargetValues[i]) |
479 | 0 | bIsTarget = TRUE; |
480 | 0 | } |
481 | 0 | } |
482 | |
|
483 | 0 | if (bIsTarget) |
484 | 0 | { |
485 | 0 | pafProximity[iPixel] = 0.0; |
486 | 0 | panNearX[iPixel] = iPixel; |
487 | 0 | panNearY[iPixel] = iLine; |
488 | 0 | continue; |
489 | 0 | } |
490 | | |
491 | | /* -------------------------------------------------------------------- |
492 | | */ |
493 | | /* Are we near(er) to the closest target to the above (below) */ |
494 | | /* pixel? */ |
495 | | /* -------------------------------------------------------------------- |
496 | | */ |
497 | 0 | double dfNearDistSq = std::max(dfMaxDist, static_cast<double>(nXSize)) * |
498 | 0 | std::max(dfMaxDist, static_cast<double>(nXSize)) * |
499 | 0 | 2.0; |
500 | |
|
501 | 0 | if (panNearX[iPixel] != -1) |
502 | 0 | { |
503 | 0 | const double dfDistSq = SquareDistance(panNearX[iPixel], iPixel, |
504 | 0 | panNearY[iPixel], iLine); |
505 | |
|
506 | 0 | if (dfDistSq < dfNearDistSq) |
507 | 0 | { |
508 | 0 | dfNearDistSq = dfDistSq; |
509 | 0 | } |
510 | 0 | else |
511 | 0 | { |
512 | 0 | panNearX[iPixel] = -1; |
513 | 0 | panNearY[iPixel] = -1; |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | | /* -------------------------------------------------------------------- |
518 | | */ |
519 | | /* Are we near(er) to the closest target to the left (right) */ |
520 | | /* pixel? */ |
521 | | /* -------------------------------------------------------------------- |
522 | | */ |
523 | 0 | const int iLast = iPixel - iStep; |
524 | |
|
525 | 0 | if (iPixel != iStart && panNearX[iLast] != -1) |
526 | 0 | { |
527 | 0 | const double dfDistSq = |
528 | 0 | SquareDistance(panNearX[iLast], iPixel, panNearY[iLast], iLine); |
529 | |
|
530 | 0 | if (dfDistSq < dfNearDistSq) |
531 | 0 | { |
532 | 0 | dfNearDistSq = dfDistSq; |
533 | 0 | panNearX[iPixel] = panNearX[iLast]; |
534 | 0 | panNearY[iPixel] = panNearY[iLast]; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | /* -------------------------------------------------------------------- |
539 | | */ |
540 | | /* Are we near(er) to the closest target to the topright */ |
541 | | /* (bottom left) pixel? */ |
542 | | /* -------------------------------------------------------------------- |
543 | | */ |
544 | 0 | const int iTR = iPixel + iStep; |
545 | |
|
546 | 0 | if (iTR != iEnd && panNearX[iTR] != -1) |
547 | 0 | { |
548 | 0 | const double dfDistSq = |
549 | 0 | SquareDistance(panNearX[iTR], iPixel, panNearY[iTR], iLine); |
550 | |
|
551 | 0 | if (dfDistSq < dfNearDistSq) |
552 | 0 | { |
553 | 0 | dfNearDistSq = dfDistSq; |
554 | 0 | panNearX[iPixel] = panNearX[iTR]; |
555 | 0 | panNearY[iPixel] = panNearY[iTR]; |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | /* -------------------------------------------------------------------- |
560 | | */ |
561 | | /* Update our proximity value. */ |
562 | | /* -------------------------------------------------------------------- |
563 | | */ |
564 | 0 | if (panNearX[iPixel] != -1 && |
565 | 0 | (pdfSrcNoDataValue == nullptr || |
566 | 0 | panSrcScanline[iPixel] != *pdfSrcNoDataValue) && |
567 | 0 | dfNearDistSq <= dfMaxDist * dfMaxDist && |
568 | 0 | (pafProximity[iPixel] < 0 || |
569 | 0 | dfNearDistSq < static_cast<double>(pafProximity[iPixel]) * |
570 | 0 | static_cast<double>(pafProximity[iPixel]))) |
571 | 0 | { |
572 | 0 | pafProximity[iPixel] = static_cast<float>(sqrt(dfNearDistSq)); |
573 | 0 | } |
574 | 0 | } |
575 | |
|
576 | 0 | return CE_None; |
577 | 0 | } |