Skip to content

Commit 09ff206

Browse files
committed
Merge branch 'jpsl/geojson-0.6' of https://github.com/jeremiahpslewis/GeoMakie.jl into jeremiahpslewis-jpsl/geojson-0.6
2 parents 99ff4de + c9cd684 commit 09ff206

14 files changed

+99
-83
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
2121
Colors = "0.12"
2222
Downloads = "1"
2323
GeoInterface = "0.5, 1.0"
24-
GeoJSON = "0.5"
24+
GeoJSON = "0.6"
2525
GeometryBasics = "0.4.1"
2626
ImageIO = "0.6"
2727
Makie = "0.17.1"

docs/src/index.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@ You can install it from the REPL like so:
1515
## GeoAxis
1616
Using GeoMakie.jl is straightforward, although it does assume basic knowledge of the Makie.jl ecosystem.
1717

18-
GeoMakie.jl provides an axis for plotting geospatial data, [`GeoAxis`](@ref), and also the function [`geo2basic`](@ref) that converts an output of GeoJSON to a polygon appropriate for plotting. Both are showcased in the examples below.
18+
GeoMakie.jl provides an axis for plotting geospatial data, [`GeoAxis`](@ref). Both are showcased in the examples below.
1919

2020
```@docs
2121
GeoAxis
22-
geo2basic
2322
```
2423

2524
## Gotchas
@@ -111,7 +110,7 @@ using GeoMakie.GeoJSON
111110
countries_file = download("https://datahub.io/core/geo-countries/r/countries.geojson")
112111
countries = GeoJSON.read(read(countries_file, String))
113112
114-
n = length(GeoInterface.features(countries))
113+
n = length(countries)
115114
hm = poly!(ax, countries; color= 1:n, colormap = :dense,
116115
strokecolor = :black, strokewidth = 0.5, overdraw = true,
117116
)

examples/field_and_countries.jl

+21-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@
44
using Makie, CairoMakie, GeoMakie
55
import Downloads
66
using GeoMakie.GeoJSON
7+
using GeometryBasics
8+
using GeoInterface
79

810
# https://datahub.io/core/geo-countries#curl # download data from here
911
worldCountries = GeoJSON.read(read(Downloads.download("https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json"), String))
10-
n = length(GeoMakie.GeoInterface.features(worldCountries))
12+
worldCountries = GeoMakie.geoJSONtraitParse(worldCountries)
13+
14+
# TODO: Type issue, Makie won't plot mixed vector of polygons and multipolygons
15+
worldCountries_poly = [c for c in worldCountries if GeoInterface.trait(c) == PolygonTrait()]
16+
worldCountries_multipoly = [c for c in worldCountries if GeoInterface.trait(c) == MultiPolygonTrait()]
17+
18+
n_poly = length(worldCountries_poly)
19+
n_multipoly = length(worldCountries_multipoly)
1120
lons = -180:180
1221
lats = -90:90
1322
field = [exp(cosd(l)) + 3(y/90) for l in lons, y in lats]
@@ -25,13 +34,21 @@ ax = GeoAxis(
2534
hm1 = surface!(ax, lons, lats, field; shading = false)
2635

2736
hm2 = poly!(
28-
ax, worldCountries;
29-
color= 1:n,
37+
ax, worldCountries_poly;
38+
color= 1:n_poly,
39+
colormap = Reverse(:plasma),
40+
strokecolor = :black,
41+
strokewidth = 0.25
42+
)
43+
44+
hm2 = poly!(
45+
ax, worldCountries_multipoly;
46+
color= (1:n_multipoly) .+ n_poly,
3047
colormap = Reverse(:plasma),
3148
strokecolor = :black,
3249
strokewidth = 0.25
3350
)
3451

35-
cb = Colorbar(fig[1,2]; colorrange = (1, n), colormap = Reverse(:plasma), label = "variable, color code", height = Relative(0.65))
52+
cb = Colorbar(fig[1,2]; colorrange = (1, n_poly + n_multipoly), colormap = Reverse(:plasma), label = "variable, color code", height = Relative(0.65))
3653

3754
fig

examples/italy.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
using CairoMakie, GeoMakie
22
using GeoMakie.GeoJSON
33
using Downloads
4+
using GeometryBasics
5+
using GeoInterface
6+
47
# Acquire data
58
it_states = Downloads.download("https://github.com/openpolis/geojson-italy/raw/master/geojson/limits_IT_provinces.geojson")
69
geo = GeoJSON.read(read(it_states, String))
7-
basic = GeoMakie.geo2basic(geo)
10+
basic = GeoMakie.geoJSONtraitParse(geo)
811

912
fig = Figure()
1013
ga = GeoAxis(fig[1, 1]; dest = "+proj=ortho +lon_0=12.5 +lat_0=42", lonlims=(12, 13), latlims = (40, 44))

examples/old/tick_locator_experiment.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ function test_tickpad()
6666
xlimits = (lmin[1], lmax[1])
6767
ylimits = (lmin[2], lmax[2])
6868

69-
_xtickvalues, _xticklabels = Makie.MakieLayout.get_ticks(LinearTicks(7), identity, GeoMakie.geoformat_ticklabels, xlimits...)
70-
_ytickvalues, _yticklabels = Makie.MakieLayout.get_ticks(LinearTicks(7), identity, GeoMakie.geoformat_ticklabels, ylimits...)
69+
_xtickvalues, _xticklabels = Makie.get_ticks(LinearTicks(7), identity, GeoMakie.geoformat_ticklabels, xlimits...)
70+
_ytickvalues, _yticklabels = Makie.get_ticks(LinearTicks(7), identity, GeoMakie.geoformat_ticklabels, ylimits...)
7171

7272
_xtickpos_in_inputspace = Point2f.(_xtickvalues, ylimits[1])
7373
_ytickpos_in_inputspace = Point2f.(xlimits[1], _ytickvalues)

examples/specialized/graph_on_usa.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ states_geo = GeoJSON.read(read(states, String))
88
# Get rid of Alaska
99
filter!((x->!(x.properties["name"] ("Alaska", "Hawaii", "Puerto Rico"))), states_geo.features)
1010

11-
n = length(GeoInterface.features(states_geo))
11+
n = length(states_geo)
1212

1313
#g = wheel_graph(10)
1414
gpos = Dict(

examples/world_population.jl

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# This example was contributed by Martijn Visser (@visr)
22
using Makie, CairoMakie, GeoMakie
33
using GeoMakie.GeoJSON
4+
using GeometryBasics
45
using Downloads
56

67
source = "+proj=longlat +datum=WGS84"
@@ -19,10 +20,12 @@ ga.yticklabelsvisible[] = false
1920
url = "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/"
2021
land = Downloads.download(url * "ne_110m_land.geojson")
2122
land_geo = GeoJSON.read(read(land, String))
23+
land_geo = GeoMakie.geoJSONtraitParse(land_geo)
2224
poly!(ga, land_geo, color=:black)
2325

2426
pop = Downloads.download(url * "ne_10m_populated_places_simple.geojson")
2527
pop_geo = GeoJSON.read(read(pop, String))
26-
scatter!(ga, GeoMakie.geo2basic(pop_geo), color="lightgrey", markersize=1.2)
28+
pop_geo = GeoMakie.geoJSONtraitParse(pop_geo)
29+
scatter!(ga, pop_geo, color="lightgrey", markersize=1.2)
2730

2831
fig

src/GeoMakie.jl

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ using GeometryBasics, Colors, ImageIO
99
using Makie
1010

1111
import Makie: convert_arguments, convert_attribute, to_value, automatic
12-
using Makie.MakieLayout, Makie.FileIO, Makie.MakieLayout.GridLayoutBase, Makie.DocStringExtensions
13-
using Makie.MakieLayout: Formatting
14-
using Makie.MakieLayout.GridLayoutBase: Side
12+
using Makie, Makie.FileIO, Makie.GridLayoutBase, Makie.DocStringExtensions
13+
using Makie: Formatting
14+
using Makie.GridLayoutBase: Side
1515

1616
using GeoJSON
17-
using GeoInterface: GeoInterface, coordinates, AbstractPolygon, AbstractMultiPolygon, features, geometry
17+
using GeoInterface: GeoInterface, coordinates, getfeature
18+
using GeometryBasics: Polygon, MultiPolygon
1819
export GeoInterface
1920

2021

@@ -33,6 +34,7 @@ Makie.to_colormap(::Nothing) = nothing
3334
# Resolve import conflicts
3435
import Makie: rotate! # use LinearAlgebra.rotate! otherwise
3536

37+
include("patch.jl")# TODO: Remove this!
3638
include("conversions.jl")
3739
include("data.jl")
3840
include("utils.jl")
@@ -44,6 +46,6 @@ export FileIO
4446

4547
include("geoaxis.jl")
4648

47-
export GeoAxis, geo2basic, datalims, datalims!, automatic
49+
export GeoAxis, datalims, datalims!, automatic
4850

4951
end # module

src/conversions.jl

+2-54
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,10 @@
11
# # Helper functions
22
to_point2(a::Vector{<: Number}) = Point2f(a[1], a[2])
33

4-
"""
5-
geo2basic(obj::GeoInterface)
6-
7-
Converts any GeoInterface object to the equivalent GeometryBasics, which can be plotted by Makie.
8-
"""
9-
function geo2basic(poly::GeoInterface.AbstractPolygon)
10-
polygon_coordinates = GeoInterface.coordinates(poly)
11-
linestrings = map(x-> to_point2.(x), polygon_coordinates)
12-
return GeometryBasics.Polygon(linestrings[1], linestrings[2:end])
13-
end
14-
15-
function geo2basic(poly::Vector{Vector{Vector{Float64}}})
16-
linestrings = map(x-> to_point2.(x), poly)
17-
return GeometryBasics.Polygon(linestrings[1], linestrings[2:end])
18-
end
19-
20-
function geo2basic(linestring::Vector{Vector{Float64}})
21-
return to_point2.(linestring)
22-
end
23-
24-
function geo2basic(point::GeoInterface.AbstractPoint)
25-
return to_point2(GeoInterface.coordinates(point))
26-
end
27-
28-
function geo2basic(multi_poly::GeoInterface.AbstractMultiPolygon)
29-
polygons = GeoInterface.coordinates(multi_poly)
30-
return geo2basic.(polygons)
31-
end
32-
33-
geo2basic(feature::GeoInterface.Feature) = geo2basic(GeoInterface.geometry(feature))
34-
function geo2basic(feature::GeoInterface.AbstractLineString)
35-
return GeometryBasics.LineString(geo2basic(GeoInterface.coordinates(feature)))
36-
end
37-
38-
to_multipoly(poly::Polygon) = GeometryBasics.MultiPolygon([poly])
39-
to_multipoly(mp::MultiPolygon) = mp
40-
to_multipoly(any) = GeometryBasics.MultiPolygon(any)
41-
42-
# Only converts polygons and multipolygons
43-
function geo2basic(fc::GeoInterface.AbstractFeatureCollection)
44-
return map(geo2basic, GeoInterface.features(fc))
45-
end
46-
47-
function convert_arguments(P::Type{<: Union{Poly, Mesh}}, geom::GeoInterface.AbstractGeometry)
48-
return convert_arguments(P, geo2basic(geom))
49-
end
50-
51-
function convert_arguments(P::Type{<:Poly}, geom::GeoInterface.AbstractFeatureCollection)
52-
return convert_arguments(P, to_multipoly.(geo2basic(geom)))
53-
end
54-
55-
564
# set the default plot type for Vectors of polygons,
575
# so that they are plotted using the most efficient method!
58-
plottype(::Vector{<: GeoInterface.AbstractMultiPolygon}) = Mesh
59-
plottype(::Vector{<: GeoInterface.AbstractPolygon}) = Mesh
6+
plottype(::Vector{<: GeometryBasics.MultiPolygon}) = Mesh
7+
plottype(::Vector{<: GeometryBasics.Polygon}) = Mesh
608

619
# Define a specialized conversion function for images with intervals
6210
# This means that one can plot images with intervals into GeoMakie

src/data.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ end
1111
function coastlines()
1212
return get!(LOAD_CACHE, "coastlines") do
1313
geometry = GeoJSON.read(read(assetpath("vector", "110m_coastline.geojson"), String))
14-
return geo2basic(geometry)
14+
return geometry
1515
end
1616
end
1717

1818

1919
function land()
2020
return get!(LOAD_CACHE, "land") do
2121
geometry = GeoJSON.read(read(assetpath("vector", "110m_land.geojson"), String))
22-
return geo2basic(geometry)
22+
return geometry
2323
end
2424
end

src/geoaxis.jl

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Makie: left, right, top, bottom
2-
using Makie.MakieLayout: height, width
2+
using Makie: height, width
33

44
"""
55
GeoAxis(fig_or_scene; kwargs...) → ax::Axis
@@ -121,8 +121,7 @@ function GeoAxis(args...;
121121
Makie.Observables.connect!(ax.scene.transformation.transform_func, _transformation)
122122

123123
# Plot coastlines
124-
coast_line = Makie.convert_arguments(PointBased(), GeoMakie.coastlines())[1]
125-
124+
coast_line = GeoMakie.geoJSONtraitParse(GeoMakie.coastlines())
126125
coastplot = lines!(ax, coast_line; color = :black, coastline_attributes...)
127126
translate!(coastplot, 0, 0, 99) # ensure they are on top of other plotted elements
128127
xprot = ax.xaxis.protrusion[]
@@ -204,11 +203,11 @@ function draw_geoticks!(ax::Axis, hijacked_observables, line_density, remove_ove
204203
xlimits[] = (lmin[1], lmax[1])
205204
ylimits[] = (lmin[2], lmax[2])
206205

207-
_xtickvalues, _xticklabels = Makie.MakieLayout.get_ticks(xticks, identity, xtickformat, xlimits[]...)
208-
_ytickvalues, _yticklabels = Makie.MakieLayout.get_ticks(yticks, identity, ytickformat, ylimits[]...)
206+
_xtickvalues, _xticklabels = Makie.get_ticks(xticks, identity, xtickformat, xlimits[]...)
207+
_ytickvalues, _yticklabels = Makie.get_ticks(yticks, identity, ytickformat, ylimits[]...)
209208

210-
_xminortickvalues = Makie.MakieLayout.get_minor_tickvalues(xminor, identity, _xtickvalues, xlimits[]...)
211-
_yminortickvalues = Makie.MakieLayout.get_minor_tickvalues(yminor, identity, _ytickvalues, ylimits[]...)
209+
_xminortickvalues = Makie.get_minor_tickvalues(xminor, identity, _xtickvalues, xlimits[]...)
210+
_yminortickvalues = Makie.get_minor_tickvalues(yminor, identity, _ytickvalues, ylimits[]...)
212211

213212
_xtickpos_in_inputspace = Point2f.(_xtickvalues, ylimits[][1])
214213
_ytickpos_in_inputspace = Point2f.(xlimits[][1], _ytickvalues)

src/patch.jl

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using GeoInterface
2+
using GeometryBasics
3+
4+
# Coerce all coordinates to Floats
5+
#### Hack for type inference issues for integers, related to ####################################
6+
#### FIX FOR https://github.com/JuliaGeometry/GeometryBasics.jl/issues/142 #####################
7+
#### Should be fixed by https://github.com/JuliaGeometry/GeometryBasics.jl/pull/173, perhaps. ##
8+
function GeoInterface.convert(::Type{LineString}, type::LineStringTrait, geom)
9+
dim = Int(ncoord(geom))
10+
return LineString([Point{dim}(GeoInterface.coordinates(p) * 1.0) for p in getgeom(geom)])
11+
end
12+
13+
function GeoInterface.convert(::Type{Point}, type::PointTrait, geom)
14+
dim = Int(ncoord(geom))
15+
return Point{dim}(GeoInterface.coordinates(geom) * 1.0)
16+
end
17+
### Hack over...
18+
19+
trait_type_pairs = [PolygonTrait() Polygon;
20+
MultiPolygonTrait() MultiPolygon;
21+
LineStringTrait() LineString;
22+
PointTrait() Point]
23+
24+
# Convert all geoJSON objects to the appropriate GeometryBasics type based on trait
25+
function geoJSONtraitParse(feature::GeoJSON.Feature)
26+
geometry = GeoInterface.geometry(feature)
27+
trait_matches = trait_type_pairs[:,1] .== (GeoInterface.trait(geometry),)
28+
29+
if sum(trait_matches) != 1
30+
@warn "GeoMakie.geoJSONtraitParse: Unknown geometry type $(GeoInterface.trait(geometry))"
31+
return nothing
32+
end
33+
34+
return GeoInterface.convert(trait_type_pairs[trait_matches, 2][1], geometry)
35+
end
36+
37+
function geoJSONtraitParse(featureCollection::GeoJSON.FeatureCollection)
38+
Vector{}
39+
return [geoJSONtraitParse(f) for f in featureCollection]
40+
end

src/utils.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ function latitude_format(nums)
198198
end
199199

200200
function _replace_if_automatic(typ::Type{T}, attribute::Symbol, auto) where T
201-
default_attr_vals = Makie.MakieLayout.default_attribute_values(T, nothing)
201+
default_attr_vals = Makie.default_attribute_values(T, nothing)
202202

203203
if to_value(get(default_attr_vals, attribute, automatic)) == automatic
204204
return auto

test/runtests.jl

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using GeoMakie, CairoMakie, Test
1+
using GeoMakie, GeometryBasics, CairoMakie, Test
22

33
Makie.set_theme!(Theme(
44
Heatmap = (rasterize = 5,),
@@ -18,6 +18,11 @@ Makie.set_theme!(Theme(
1818
# display(fig)
1919
end
2020

21+
@testset "geoJSONtraitParse" begin
22+
@test GeoMakie.geoJSONtraitParse(GeoMakie.coastlines()) isa Vector
23+
@test GeoMakie.geoJSONtraitParse(GeoMakie.coastlines()[1]) isa GeometryBasics.LineString
24+
end
25+
2126
@testset "Examples" begin
2227
geomakie_path = dirname(dirname(pathof(GeoMakie)))
2328
examples = readdir(joinpath(geomakie_path, "examples"); join = true)

0 commit comments

Comments
 (0)