Skip to content

Is it on a GeoRegion Boundary?

Sometimes, we don't just want information on whether a point is inside a GeoRegion. Sometimes, it is important to know if something is on the boundary of a GeoRegion. This would prove useful in determining if the shapes of two different GeoRegions are equivalent to each other.

Point2 Type

We use the Point2 Type from the package GeometryBasics.jl, which is reexported by GeoRegions.jl, as an easy way to denote points. This also allows us to use the package GeometryOps.jl to determine if a point is on a GeoRegion boundary.

julia
using GeoRegions
using DelimitedFiles
import CairoMakie: Figure, Axis, lines!, scatter!, resize_to_layout!

download("https://raw.githubusercontent.com/natgeo-wong/GeoPlottingData/main/coastline_resl.txt","coast.cst")
coast = readdlm("coast.cst",comments=true)
clon  = coast[:,1]
clat  = coast[:,2]
nothing

Is a Point on a GeoRegion Boundary?

As an example, let us test if a point is on the perimeter of the GeoRegion AR6_EAO, defined in the blue bounding box below.

julia
geo = GeoRegion("AR6_EAO")
The GeoRegion AR6_EAO has the following properties:
    Region ID     (ID) : AR6_EAO
    Parent ID    (pID) : GLB
    Name        (name) : Equatorial Atlantic Ocean
    Bounds   (N,S,E,W) : 7.6, -10.0, 8.0, -50.0
    Rotation       (θ) : 0.0
    File Path   (path) : /home/runner/work/GeoRegions.jl/GeoRegions.jl/src/.files/AR6/AR6_EAO.json
    Centroid (geometry.centroid) : [-19.31990102509721, -1.27790738776953]
    Shape       (geometry.shape) : Vector{Point{2, Float64}}(8)

Next, we select a point along the border

julia
A = Point(-25,7.6)
B = Point(335,7.6)
C = Point(-20,5)
D = Point(-45,-7.5)
2-element Point{2, Float64} with indices SOneTo(2):
 -45.0
  -7.5

Here, we note that A and B

julia
slon,slat = coordinates(geo)
aspect = (maximum(slon)-minimum(slon))/(maximum(slat)-minimum(slat))
fig = Figure()
ax = Axis(
    fig[1,1],width=750,height=750/aspect,
    limits=(minimum(slon)-2,maximum(slon)+2,minimum(slat)-2,maximum(slat)+2),
    xlabel = "Longitude / º", ylabel = "Latitude / º"
)
lines!(ax,clon,clat,color=:black,linewidth=3)
lines!(ax,slon,slat,linewidth=5)
scatter!(ax,A,markersize=20)
scatter!(ax,C,markersize=20)
scatter!(ax,D,markersize=20)
resize_to_layout!(fig)
fig

By eye it is easy to see that Point A is on the GeoRegion perimeter. However, C and D are not. Let us now see if we are able to corroborate this with GeoRegions.jl using the function on()

julia
on(A,geo), # Point A
on(C,geo), # Point C
on(D,geo)  # Point D
(true, false, false)

But what about Point B? Point B is also very obviously on the bounds of the GeoRegion, it is simply that the longitude of Point B is shifted by 360º from A such that it is now in the (0,360) coordinates frame. We see that this is of no concern to GeoRegions.jl, which can detect that it is within the bounds of the GeoRegion anyway.

julia
on(B,geo)
true

See the API here

Is a GeoRegion on a GeoRegion? (i.e., Are their Shapes the Same?)

We can also use the on() function to determine if the perimeter of a GeoRegion is on the perimeter of another. Or, in simpler terms, if two GeoRegions define the same shape. For example:

julia
on(geo,geo)
true

See the API here

Which is obvious because we are comparing a GeoRegion against itself. However, let us try something a bit more complicated.

1. circshift() the points defining the GeoRegion

In this test case, we use circshift() to change the starting and ending coordinates of the shape. Therefore, we are defining the same region, just with different start and ending points.

julia
lon,lat = coordinates(geo)
pop!(lon); lon = circshift(lon,2); lon = vcat(lon,lon[1])
pop!(lat); lat = circshift(lat,2); lat = vcat(lat,lat[1])
geo2 = GeoRegion(lon,lat)
The GeoRegion  has the following properties:
    Region ID     (ID) : 
    Parent ID    (pID) : 
    Name        (name) : 
    Bounds   (N,S,E,W) : 7.6, -10.0, 8.0, -50.0
    Rotation       (θ) : 0.0
    File Path   (path) : /home/runner/.georegions/.json
    Centroid (geometry.centroid) : [-19.31990102509721, -1.27790738776953]
    Shape       (geometry.shape) : Vector{Point{2, Float64}}(8)

So here, we have circshifted the lon and lat values used to define the GeoRegion such that instead of the start/end points being at (-34,-10), now the start/end points are at (-50,0).

We compare the shapes of the two GeoRegions:

julia
on(geo,geo2),
on(geo2,geo)
(true, true)

And see that the GeoRegions define the same area.

Order of GeoRegions does not matter in on()

The order should not matter, because in the on() function, the GeoRegions are both compared against each other. So on(geo,geo2) = on(geo2,geo). If you find any edge cases where this is not the case, please let me know and I will look into it.

2. Shifting the GeoRegion by 360º

In this test case, we shift the lon of a given GeoRegion by 360º, so that it is on a (0,360) grid instead of a (-180,180) grid.

julia
geo3 = GeoRegion("AR6_WNA")
The GeoRegion AR6_WNA has the following properties:
    Region ID     (ID) : AR6_WNA
    Parent ID    (pID) : GLB
    Name        (name) : Western North America
    Bounds   (N,S,E,W) : 50.0, 33.8, -105.0, -130.0
    Rotation       (θ) : 0.0
    File Path   (path) : /home/runner/work/GeoRegions.jl/GeoRegions.jl/src/.files/AR6/AR6_WNA.json
    Centroid (geometry.centroid) : [-115.7352941176471, 42.376470588235314]
    Shape       (geometry.shape) : Vector{Point{2, Float64}}(5)

In this case, the longitude is all <0º, so we can simply add 360º to the longitude and transform it from a 180ºW-180ºE grid to a 0º-360ºE grid.

julia
lon,lat = coordinates(geo3); lon = lon .+ 360
geo4 = GeoRegion(lon,lat)
The GeoRegion  has the following properties:
    Region ID     (ID) : 
    Parent ID    (pID) : 
    Name        (name) : 
    Bounds   (N,S,E,W) : 50.0, 33.8, 255.0, 230.0
    Rotation       (θ) : 0.0
    File Path   (path) : /home/runner/.georegions/.json
    Centroid (geometry.centroid) : [244.26470588235313, 42.376470588235335]
    Shape       (geometry.shape) : Vector{Point{2, Float64}}(5)

We compare the shapes of the two GeoRegions:

julia
on(geo3,geo4)
true

And we see that the GeoRegions define the same area.