bartromgens / geojsoncontour Goto Github PK
View Code? Open in Web Editor NEWConvert matplotlib contour plots to geojson
License: MIT License
Convert matplotlib contour plots to geojson
License: MIT License
Thanks for this solution ๐
I had tried converting contouf plot to geojson polygens, but there seemed some bug in displaying the output geojson by leaflet.js, becasue the polygens are finaly rendered with SVG in the browser. The path defined in SVG has it's own rules and some complicate polygens derived from matplotlib can not be simply converted to geojson polygens correctly. I have not yet figured it out.
import numpy as np
import simplejson as json
from geojson import Polygon, Feature, FeatureCollection
def contourf_to_geojson(cs):
""" cs = plt.contourf(...) """
allsegs = cs.allsegs
levels = cs.levels
tcolors = cs.tcolors
features = []
for level in range(len(allsegs)):
seg = allsegs[level]
if seg:
for _ in range(len(seg)):
poly = np.round(seg[_], 3).tolist()
poly = poly[::-1] # necessary
poly = Polygon([poly])
feature = Feature(geometry=poly, properties={"level": level, "color": tcolors[level][0]})
features.append(feature)
features = FeatureCollection(features)
features = json.dumps(features)
with open('features.json', 'w') as fc:
fc.write(features)
I've installed the package with pip. When I try to import geojsoncontour
I get this error:
Traceback (most recent call last):
File "/home/al/.pyenv/versions/anaconda3-5.1.0/lib/python3.6/runpy.py", line 183, in _run_module_as_main
mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
File "/home/al/.pyenv/versions/anaconda3-5.1.0/lib/python3.6/runpy.py", line 142, in _get_module_details
return _get_module_details(pkg_main_name, error)
File "/home/al/.pyenv/versions/anaconda3-5.1.0/lib/python3.6/runpy.py", line 109, in _get_module_details
__import__(pkg_name)
File "/home/al/.pyenv/versions/anaconda3-5.1.0/lib/python3.6/site-packages/geojsoncontour/__init__.py", line 1, in <module>
from .contour import contour_to_geojson
File "/home/al/.pyenv/versions/anaconda3-5.1.0/lib/python3.6/site-packages/geojsoncontour/contour.py", line 8, in <module>
from geojsoncontour.utilities.multipoly import MP, keep_high_angle, set_contourf_properties
ModuleNotFoundError: No module named 'geojsoncontour.utilities'
Apparently the utilities
directory doesn't come bundled in the archive.
Adding it to the packages
entry of setup.py
seems to solve it. I'll send a PR shortly.
the example as stated in the readme of the usage contour_to_json function doesn't work.
As the function call doesn't include this parameter as default, the value is None.
And later in the function it makes a comparison and throws the following stack trace:
gjs = geojsoncontour.contour_to_geojson(contour=cnt, ndigits=3, unit='m')
Traceback (most recent call last):
File "/home/vichoko/Apps/pycharm-2018.2.4/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
File "/home/vichoko/anaconda3/envs/rs_api/lib/python3.6/site-packages/geojsoncontour/contour.py", line 24, in contour_to_geojson
coordinates = keep_high_angle(v, min_angle_deg)
File "/home/vichoko/anaconda3/envs/rs_api/lib/python3.6/site-packages/geojsoncontour/utilities/multipoly.py", line 55, in keep_high_angle
if diff_angle > min_angle_deg:
TypeError: '>' not supported between instances of 'float' and 'NoneType'
My GeoJSON wasn't rendering properly in combination with geojson-vt, and after filing a bug with them, they identified that the GeoJSON from geojsoncontour was not valid.
The fundamental problem is that many of the outer polygon rings are marked in the GeoJSON as being interior rings.
o For Polygons with more than one of these rings, the first MUST be
the exterior ring, and any others MUST be interior rings. The
exterior ring bounds the surface, and the interior rings (if
present) bound holes within the surface.
Ignoring the GeoJSON spec and detecting inner/outer rings by winding rather than by ring order within the geometry gives the intended result:
Can this be fixed within geojsoncontour?
I would like to add an option to return a Python data structure instead of a string. My use case is adding a GeoJSON layer to ipyleaflet which takes a Python structure. Of course it's very easy to call json.loads on the output of geojsoncontour, but it adds unnecessary serialization/deserialization. Maybe we could add a serialize
keyword argument which would be True
by default?
When I try to install geojsoncontour by using pip, it did not install the utilities package.
I need to add the utility from this repository by myself.
Hello!
I am using folium to plot contours on top of map by converting contours using this library.
Is it possible to retain clip paths information? this will help to plot clipped contours using shapefile on map background
As I coulnd't figure out how exactly to start a pull request here (never done that before actually),
I'm going to post my customized version of the function get_contourf_levels()
.
This will allow for passing a specific float-format of desire which will affect the labeling of the contours.
The formatting of these string-labels itself has also been improved.
def get_contourf_levels(levels, extend, float_fmt="%.2f"):
# Latest implementation using f-strings and improved formatting of all ranges
mid_levels = [
f"({float_fmt % levels[i]} - {float_fmt % levels[i+1]})"
for i in range(len(levels) - 1)
]
# Previous implementation:
#mid_levels = [float_fmt % levels[i] + '-' + float_fmt % levels[i+1] for i in range(len(levels)-1)]
# i) Decide whether to modify the mid-levels list
if extend == 'both':
return [
f"< {float_fmt % levels[0]}", *mid_levels,
f"> {float_fmt % levels[-1]}"
]
elif extend == 'max':
return [*mid_levels, f"> {float_fmt % levels[-1]}"]
elif extend == 'min':
return [f"< {float_fmt % levels[0]}", *mid_levels]
# ii) Return unchanged
else:
return mid_levels
To pass the new parameter float_fmt
, the two contourf-geojson
functions also need to be adjusted a bit:
def contourf_to_geojson_overlap(contourf,
geojson_filepath=None,
min_angle_deg=None,
ndigits=5,
unit='',
stroke_width=1,
fill_opacity=.9,
geojson_properties=None,
strdump=False,
serialize=True,
float_fmt="%.2f"):
"""Transform matplotlib.contourf to geojson with overlapping filled contours."""
polygon_features = []
contourf_idx = 0
# Pass (custom) float format to extract the contour-levels
contourf_levels = get_contourf_levels(contourf.levels,
contourf.extend,
float_fmt=float_fmt)
for collection in contourf.collections:
color = collection.get_facecolor()
for path in collection.get_paths():
for coord in path.to_polygons():
if min_angle_deg:
coord = keep_high_angle(coord, min_angle_deg)
coord = np.around(coord, ndigits) if ndigits else coord
polygon = Polygon(coordinates=[coord.tolist()])
fcolor = rgb2hex(color[0])
properties = set_contourf_properties(
stroke_width, fcolor, fill_opacity,
contourf_levels[contourf_idx], unit)
if geojson_properties:
properties.update(geojson_properties)
feature = Feature(geometry=polygon, properties=properties)
polygon_features.append(feature)
contourf_idx += 1
feature_collection = FeatureCollection(polygon_features)
return _render_feature_collection(feature_collection, geojson_filepath,
strdump, serialize)
def contourf_to_geojson(contourf,
geojson_filepath=None,
min_angle_deg=None,
ndigits=5,
unit='',
stroke_width=1,
fill_opacity=.9,
fill_opacity_range=None,
geojson_properties=None,
strdump=False,
serialize=True,
float_fmt="%.2f"):
"""Transform matplotlib.contourf to geojson with MultiPolygons."""
if fill_opacity_range:
variable_opacity = True
min_opacity, max_opacity = fill_opacity_range
opacity_increment = (max_opacity - min_opacity) / len(contourf.levels)
fill_opacity = min_opacity
else:
variable_opacity = False
polygon_features = []
# Pass (custom) float format to extract the contour-levels
contourf_levels = get_contourf_levels(contourf.levels,
contourf.extend,
float_fmt=float_fmt)
for coll, level in zip(contourf.collections, contourf_levels):
color = coll.get_facecolor()
muli = MP(coll, min_angle_deg, ndigits)
polygon = muli.mpoly()
fcolor = rgb2hex(color[0])
properties = set_contourf_properties(stroke_width, fcolor,
fill_opacity, level, unit)
if geojson_properties:
properties.update(geojson_properties)
feature = Feature(geometry=polygon, properties=properties)
polygon_features.append(feature)
if variable_opacity:
fill_opacity += opacity_increment
feature_collection = FeatureCollection(polygon_features)
return _render_feature_collection(feature_collection, geojson_filepath,
strdump, serialize)
Hello from Norway !
It looks like, since matplotlib 3.8.?, the geojson files computed with geojsoncontour are not always following the right-hand rule. This was probably taken care of by matplotlib before, but recent release notes suggest some "revert contour" deprecation.
Hi,
I'm trying to install the 0.2.1 release of this package from PyPI but apparently there's an issue with the wheel filename.
$ pip install -v geojsoncontour==0.2.1
Collecting geojsoncontour==0.2.1
1 location(s) to search for versions of geojsoncontour:
* https://pypi.python.org/simple/geojsoncontour/
[...]
Skipping link https://pypi.python.org/packages/fd/08/24a58eb329126dad04b7010a1132dd4fa653aaec348cd3c28484289a0949/geojsoncontour-0.2.1-py3-release-any.whl#md5=aba2a5f556b1528e0cb9594a23a76a7a (from https://pypi.python.org/simple/geojsoncontour/); it is not compatible with this Python
[...]
I think the reason why it refuses to install it is because the filename contains release
where it should contain none
. If you look at the previous wheel files, their filename was containing none
as abi tag filename part.
I can ran my Python script correctly with python command such as
$python rainfall_contour.py
Unfortunately, the same command in a shell script caused error "ModuleNotFoundError: No module named 'geojsoncontour'". geojsoncontour has been correctly installed. How to eliminate this error?
Hi,
many thanks for developing this package. Are you planning to also release is via conda package manager (e.g. conda-forge channel)? It is very easy to setup, based on your PyPi release.
Cheers,
Jonas
We use this project in pyaerocom which is developed at the Norwegian Meteorological Institute for air quality and climate model evaluation and therefore have a vested interest in this project's survival. We could contribute to the maintenance, or even take over it's development if necessary as part of our day jobs. Please let us know how we can contribute to the survival of this project. It's very useful! :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.