﻿

# Nearest coastal point

From a given coordinate, we can find the nearest point in an arbitrary area.

If white pixels represent the sea and black pixels represent land, with an arbitrary boundary between them, and we have a point on the sea, we can find the nearest coastal point.

Scripts on this page assume that the version of ImageMagick in %IMDEV% has been built with various process modules. See Process modules.

## Method: polar distortion

This method using polar distortion works, but the nearest white method shown below is generally better.

See Polar Distortions for a discussion of unrolling images around a defined central point (and rolling them back up).

The technique is to unroll the input image, with the given sea point as the central point. The image is negated to make the sea black and the land white. Then the highest white point on the unrolled image represents the nearest coastal point. From the found (x,y) coordinates of this white point on the unrolled image, we easily calculate (r,θ) for the distance r and angle theta from the central point in the input image and hence the (x',y') coordinates of the found point on the input image.

r = y * d/h
θ = x * 2.pi/w     ... θ in radians
x' = x - r.sin(θ)
y' = y - r.cos(θ)

... where w and h are the image width and height, and d is the distance from the central point to the furthest corner.

For example, suppose we have is_easy_mainland.png from the Islands page, and want to find the nearest coastal point to the coordinate 64,80.

 is_easy_mainland.png ``` %IMDEV%convert ^ is_easy_mainland.png ^ -negate ^ -virtual-pixel Black ^ -distort depolar "-1,0,64,80" ^ -threshold 50%% ^ -process onewhite ^ nc_ex1.png ``` `onewhite: 87,27` The command gives (x,y) in unrolled space. From this, (r,θ) and (x',y') can be calculated.

As x and y are integers, the precision of the calculated (x',y') coordinates is limited, and is improved by supersampling.

### The script nearCoast.bat

The script nearCoast.bat does this, calculating ncRAD, ncTHETA, ncCST_X and ncCST_Y. If an output filename is supplied, the script also makes a debugging image showing a circle centred on the given white point and passing through the coastal point, and a line between the two points.

The given coordinates can be specified as pixels, or as a percentage of the image width and height.

 ```call %PICTBAT%nearCoast ^ is_easy_mainland.png . . . nc_nc1.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=85.2159 ncTHETA=0 ncCST_CX=159.5 ncCST_CY=99.5 ncCST_X=159.5 ncCST_Y=14.2841 ``` ```call %PICTBAT%nearCoast ^ is_easy_mainland.png 20%%%% 40%%%% . nc_nc2.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=38.2159 ncTHETA=1.53153 ncCST_CX=63.8 ncCST_CY=79.6 ncCST_X=25.6136 ncCST_Y=78.0998 ``` Repeat the previous, with more precision. ```set ncSUP_SAMP=4 call %PICTBAT%nearCoast ^ is_easy_mainland.png 20%%%% 40%%%% . nc_nc3.png set ncSUP_SAMP= echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=38.2159 ncTHETA=1.71806 ncCST_CX=63.8 ncCST_CY=79.6 ncCST_X=25.9977 ncCST_Y=85.2075 ``` ```call %PICTBAT%nearCoast ^ is_easy_mainland.png 60%%%% 70%%%% . nc_nc4.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=46.1613 ncTHETA=2.94524 ncCST_CX=191.4 ncCST_CY=139.3 ncCST_X=182.394 ncCST_Y=184.574 ``` Repeat the previous, with more precision. ```set ncSUP_SAMP=4 call %PICTBAT%nearCoast ^ is_easy_mainland.png 60%%%% 70%%%% . nc_nc5.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% set ncSUP_SAMP=``` ```ncRAD=45.5695 ncTHETA=3.11214 ncCST_CX=191.4 ncCST_CY=139.3 ncCST_X=190.058 ncCST_Y=184.85 ``` As the previous two examples show, increasing precision can have a significant impact.

The script is general-purpose: it will find the nearest black pixel to a given white coordinate, even if there are multiple clusters of black pixels.

 Make a sample: ```%IM%convert ^ is_easy_mainland.png ^ -background Black -gravity Center -extent 200%%x200%% ^ -resize 25%% ^ -negate ^ ( +clone ) +append +repage ^ ( +clone ) -append +repage ^ nc_mult1.png``` Find the coastal point nearest to (40%,40%): ```call %PICTBAT%nearCoast ^ nc_mult1.png 40%%%% 40%%%% . nc_m1.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=19.2922 ncTHETA=0.687223 ncCST_CX=127.6 ncCST_CY=79.6 ncCST_X=115.361 ncCST_Y=64.6869 ``` The sea doesn't need to be white. We might give a coordinate that is black.

 Find the coastal point nearest to (21%,25%): ```call %PICTBAT%nearCoast ^ nc_mult1.png 21%%%% 25%%%% . nc_blk1.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=0 ncTHETA=0 ncCST_CX=66.99 ncCST_CY=49.75 ncCST_X=66.99 ncCST_Y=49.75 ``` ncRAD=0 means the given coordinate is not the sea. Declare the sea is black, and find the coastal point nearest to (21%,25%): ```set ncSEA_COL=black call %PICTBAT%nearCoast ^ nc_mult1.png 21%%%% 25%%%% . nc_blk2.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` ```ncRAD=19.1269 ncTHETA=2.63108 ncCST_CX=66.99 ncCST_CY=49.75 ncCST_X=57.6441 ncCST_Y=66.4381 ``` Automatically find the sea colour, and find the coastal point nearest to (21%,25%): ```set ncSEA_COL=auto call %PICTBAT%nearCoast ^ nc_mult1.png 21%%%% 25%%%% . nc_blk3.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% set ncSEA_COL=``` ```ncRAD=0 ncTHETA=0 ncCST_CX=66.99 ncCST_CY=49.75 ncCST_X=66.99 ncCST_Y=49.75 ``` If the sea is white, the script finds the nearest point that is less than 50% intensity. Similarly, when the sea is black, it finds the nearest point that is more than 50% intensity. The found point will be the "other" colour. When multiple points are to be found from random origins, flipping between two sides of a line can be avoided by ensuring the boundary is aliased, or by finding a thin edge between the black and white, and calling this the land.

 Automatically find the sea colour, and find the coastal point nearest to (21%,25%): ```%IM%convert ^ nc_mult1.png ^ -edge 1 ^ -threshold 50%% ^ nc_edge1.png set ncSEA_COL=black call %PICTBAT%nearCoast ^ nc_edge1.png 21%%%% 25%%%% . nc_e1.png echo ncRAD=%ncRAD% ncTHETA=%ncTHETA% echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% set ncSEA_COL=``` ```ncRAD=19.1269 ncTHETA=2.61145 ncCST_CX=66.99 ncCST_CY=49.75 ncCST_X=57.3184 ncCST_Y=66.2514 ``` When the image is entirely sea, so there is no mainland:

```%IM%convert ^
-size 100x100 xc:Black ^
nc_justsea.png

call %PICTBAT%nearCoast ^
nc_edge1.png 21%%%% 25%%%% . nc_js_out.png

echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY%
echo ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y%
```
```ncRAD=0 ncTHETA=0
ncCST_CX=66.99 ncCST_CY=49.75
ncCST_X=66.99 ncCST_Y=49.75 ```

Again, we have a return of ncRAD=0. In general, this should always be checked.

This technique can also be used to find corresponding coastal points. See Correlating shapes (not yet published).

## Method: nearest white

The method shown above works. But unrolling the image takes time, the result is imprecise, and calculations are awkward.

As an alternative, we write a new process module, called nearestwhite that simply finds the nearest white pixel to the supplied coordinates.

The resulting script nearCoast2.bat is simpler and faster than nearCoast.bat. It doesn't offer supersampling because that would be of no benefit. It doesn't need to calculate RAD or THETA, so it doesn't. If there are no pixels of the required colour, nearCoast2.bat returns with ncCST_CX=-1.

nearCoast2.bat will always be quicker and more accurate than nearCoast.bat.

 ```call %PICTBAT%nearCoast2 ^ is_easy_mainland.png . . . nc_nw_nc1.png echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% ^ ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` `ncCST_CX=159.5 ncCST_CY=99.5 ncCST_X=169 ncCST_Y=15 ` ```call %PICTBAT%nearCoast2 ^ is_easy_mainland.png 20%%%% 40%%%% . nc_nw_nc2.png echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% ^ ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` `ncCST_CX=63.8 ncCST_CY=79.6 ncCST_X=25 ncCST_Y=85 ` ```call %PICTBAT%nearCoast2 ^ is_easy_mainland.png 60%%%% 70%%%% . nc_nw_nc4.png echo ncCST_CX=%ncCST_CX% ncCST_CY=%ncCST_CY% ^ ncCST_X=%ncCST_X% ncCST_Y=%ncCST_Y% ``` `ncCST_CX=191.4 ncCST_CY=139.3 ncCST_X=200 ncCST_Y=184 ` ## Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

### nearCoast.bat

```rem Given image %1 is black mainland around white "sea",
rem finds floating-point coordinates (ncX,ncY) of point on the mainland
rem    nearest to given coordinate (%2,%3).
rem If the given coord is in the white "sea", the nearest point will be on the black "coast".
@rem
@rem   %2, %3 coords of central point  [default center of %1]
@rem        Each coord may be suffixed with "%", for percentage of width or height.
@rem        Default: centre of image.
@rem   %4 proportion towards coast point (0 to 100) 
@rem   %5 output debugging image [default NULL:, no output]
@rem
@rem Also uses:
@rem   ncSUP_SAMP Factor for supersampling [default 1].
@rem   ncPREFIX prefix for temporary files
@rem   ncSEA_COL sea color: black, white or auto. Default white.
@rem     auto will choose automatically, which takes time.
@rem   nc?? automatic choice for negate
@rem   nc?? if 1, assumes %1 is already supersampled and unrolled.
@rem
@rem Returns data about nearest point:
@rem   ncRAD   distance from given coord. 0 means given coord wasn't sea.
@rem   ncTHETA bearing from given coord to found point, anti-clockwise from north.
@rem   ncCST_X } x,y coord of found point.
@rem   ncCST_Y }
@rem
@rem Last update: 16-July-2016
@rem

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 nc

if "%ncIM%"=="" set ncIM=%IMDEV%

set CENT_X=%2
set CENT_Y=%3

if "%CENT_X%"=="." set CENT_X=
if "%CENT_Y%"=="." set CENT_Y=

set BLANK_CENT=0
if "%CENT_X%"=="" if "%CENT_Y%"=="" set BLANK_CENT=1

if "%CENT_X%"=="" set CENT_X=50%%
if "%CENT_Y%"=="" set CENT_Y=50%%

set X_SUFFIX=%CENT_X:~-1%
if "%X_SUFFIX%"=="%%" (
set nX=%CENT_X:~0,-1%
set X_MULT=^(w-1^)/100
) else (
set nX=%CENT_X%
set X_MULT=1
)

set Y_SUFFIX=%CENT_Y:~-1%
if "%Y_SUFFIX%"=="%%" (
set nY=%CENT_Y:~0,-1%
set Y_MULT=^(h-1^)/100
) else (
set nY=%CENT_Y%
set Y_MULT=1
)

set PROP=%4
if "%PROP%"=="." set PROP=
if "%PROP%"=="" set PROP=100

set OUTFILE=%5
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

set SUP_SAMP=%ncSUP_SAMP%
if "%SUP_SAMP%"=="." set SUP_SAMP=
if "%SUP_SAMP%"=="" set SUP_SAMP=1

for /F "usebackq" %%L in (`%ncIM%identify ^
-ping ^
-format "WW=%%w\nHH=%%h\nnX=%%[fx:%nX%*%X_MULT%]\nnY=%%[fx:%nY%*%Y_MULT%]"
%INFILE%`) do set %%L

for /F "usebackq" %%L in (`%ncIM%identify ^
-ping ^
-format "W_SS=%%[fx:%WW%*%SUP_SAMP%]\nH_SS=%%[fx:%HH%*%SUP_SAMP%]\nnXS=%%[fx:%nX%*%SUP_SAMP%]\nnYS=%%[fx:%nY%*%SUP_SAMP%]"
xc:`) do set %%L

rem echo %0: WW=%WW%  HH=%HH%  W_SS=%W_SS%  H_SS=%H_SS%  nX=%nX%  nY=%nY%  nXS=%nXS%  nYS=%nYS%

if %BLANK_CENT%==1 (
set sUNROLL=-distort depolar "-1,0"
) else (
set sUNROLL=-distort depolar "-1,0,%nXS%,%nYS%"
)

if %SUP_SAMP%==1 (
set sSUP_UP=
) else (
set sSUP_UP=-resize "%W_SS%x%H_SS%^^^!"
)

if "%ncSEA_COL%"=="" set ncSEA_COL=white

if "%ncSEA_COL%"=="white" (
set sNEG=-negate
) else if "%ncSEA_COL%"=="black" (
set sNEG=
) else if "%ncSEA_COL%"=="auto" (
for /F "usebackq" %%L in (`%ncIM%identify ^
-format "doNEG=%%[fx:p{0,0}.intensity>0.5?1:0]" ^
%INFILE%`) do set %%L

if "!doNeg!"=="1" (
set sNEG=-negate
) else (
set sNEG=
)
) else (
exit /B 1
)

for /F "usebackq tokens=1-3 delims=:, " %%W in (`%ncIM%convert ^
%INFILE% ^
%sNEG% ^
%sSUP_UP% ^
-virtual-pixel Black ^
%sUNROLL% ^
-threshold 50%% ^
-process onewhite ^
NULL: 2^>^&1`) do (
set MAGIC=%%W
set X=%%X
set Y=%%Y
)

if not "%magic%"=="onewhite" exit /B 1

for /F "usebackq" %%L in (`%ncIM%identify ^
-ping ^
-format "dx=%%[fx:%nXS%>%W_SS%/2?%nXS%:%W_SS%-%nXS%]\ndy=%%[fx:%nYS%>%H_SS%/2?%nYS%:%H_SS%-%nYS%]"
%INFILE%`) do set %%L

for /F "usebackq" %%L in (`%ncIM%identify ^
-ping ^
xc:`) do set %%L

for /F "usebackq" %%L in (`%ncIM%identify ^
xc:`) do set %%L

if /I not "%OUTFILE%"=="NULL:" (

if "%sNEG%"=="" (
set DBG_COL=yellow
) else (
set DBG_COL=red
)

%ncIM%convert ^
%INFILE% ^
-fill None -stroke !DBG_COL! ^
-draw "line   %nX%,%nY% %CST_X%,%CST_Y%" ^
-draw "circle %nX%,%nY% %CST_X%,%CST_Y%" ^
-alpha off ^
%OUTFILE%
)

call echoRestore

@endlocal & set ncOUTFILE=%OUTFILE%& set ncRAD=%RAD%& set ncTHETA=%THETA%& set ncCST_CX=%nX%& set ncCST_CY=%nY%& set ncCST_X=%CST_X%& set ncCST_Y=%CST_Y%```

### nearCoast2.bat

```rem Given image %1 is black mainland around white "sea",
rem finds floating-point coordinates (ncX,ncY) of point on the mainland
rem    nearest to given coordinate (%2,%3).
rem If there are no white pixels, exits with ncCST_CX=-1.
rem If the given coord is in the white "sea", the nearest point will be on the black "coast".
@rem
@rem   %2, %3 coords of central point  [default center of %1]
@rem        Each coord may be suffixed with "%", for percentage of width or height.
@rem        Default: centre of image.
@rem   %4 proportion towards coast point (0 to 100) 
@rem   %5 output debugging image [default NULL:, no output]
@rem
@rem Also uses:
@rem   ncPREFIX prefix for temporary files
@rem   ncSEA_COL sea color: black, white or auto. Default white.
@rem     auto will choose automatically, which takes time.
@rem   nc?? automatic choice for negate
@rem   nc?? if 1, assumes %1 is already supersampled and unrolled.
@rem
@rem Returns data about nearest point:
@rem   ncCST_X  x coord of found point, or -1 if none.
@rem   ncCST_Y  y coord of found point.

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 nc2

if "%ncIM%"=="" set ncIM=%IMDEV%

set CENT_X=%2
set CENT_Y=%3

if "%CENT_X%"=="." set CENT_X=
if "%CENT_Y%"=="." set CENT_Y=

if "%CENT_X%"=="" set CENT_X=50%%
if "%CENT_Y%"=="" set CENT_Y=50%%

set PROP=%4
if "%PROP%"=="." set PROP=
if "%PROP%"=="" set PROP=100

set OUTFILE=%5
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=NULL:

if "%ncSEA_COL%"=="" set ncSEA_COL=white

if "%ncSEA_COL%"=="white" (
set sNEG=-negate
) else if "%ncSEA_COL%"=="black" (
set sNEG=
) else if "%ncSEA_COL%"=="auto" (
echo %0: Error: 'auto' not available.
exit /B 1
) else (
exit /B 1
)

set X=
set CX=

for /F "usebackq tokens=1-3 delims=,= " %%W in (`%ncIM%convert ^
%INFILE% ^
%sNEG% ^
-virtual-pixel Black ^
-threshold 50%% ^
-process 'nearestwhite cx %CENT_X% cy %CENT_Y% v' ^
NULL: 2^>^&1`) do (
set MAGIC=%%W
if "!MAGIC!"=="nearestwhite.cx" (
set CX=%%X
) else if "!MAGIC!"=="nearestwhite.cy" (
set CY=%%X
) else  if "!MAGIC!"=="nearestwhite:" (
set X=%%X
set Y=%%Y
)
)

if "%X%"=="" exit /B 1
if "%CX%"=="" exit /B 1

if "%X%"=="none" (
set X=-1
goto end
)

echo %0: CX=%CX% CY=%CY% X=%X% Y=%Y%

if /I not "%OUTFILE%"=="NULL:" (

if "%sNEG%"=="" (
set DBG_COL=yellow
) else (
set DBG_COL=red
)

%ncIM%convert ^
%INFILE% ^
-fill None -stroke !DBG_COL! ^
-draw "line   %CX%,%CY% %X%,%Y%" ^
-draw "circle %CX%,%CY% %X%,%Y%" ^
-alpha off ^
%OUTFILE%
)

:end

call echoRestore

@endlocal & set ncOUTFILE=%OUTFILE%& set ncCST_CX=%CX%& set ncCST_CY=%CY%& set ncCST_X=%X%& set ncCST_Y=%Y%```

All images on this page were created by the commands shown, using:

`%IM%identify -version`
```Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib```
`%IMDEV%identify -version`
```Version: ImageMagick 6.9.9-40 Q32 x86_64 2018-05-01 http://www.imagemagick.org
Features: Cipher DPC HDRI Modules OpenMP
Delegates (built-in): bzlib cairo fftw fontconfig fpx freetype jbig jng jpeg lcms ltdl lzma pangocairo png rsvg tiff webp wmf x xml zlib```

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG to JPG.

Source file for this web page is nearcoast.h1. To re-create this web page, run "procH1 nearcoast".

This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.