Giter VIP home page Giter VIP logo

geojsoncontour's Introduction

geojsoncontour

Build Status PyPI version Coverage Status
A Python 3 module to convert matplotlib contour plots to geojson. Supports both contour and contourf plots.

Designed to show geographical contour plots, created with matplotlib/pyplot, as vector layer on interactive slippy maps like OpenLayers and Leaflet.

Demo project that uses geojsoncontour: climatemaps.romgens.com

geojson contour demo usage

Installation

Install with pip,

pip install geojsoncontour

Usage

Use contour_to_geojson to create a geojson with contour lines from a matplotlib.contour plot (not filled). Use contourf_to_geojson to create a geojson with filled contours from a matplotlib.contourf plot.

Contour plot to geojson

import numpy
import matplotlib.pyplot as plt
import geojsoncontour

# Create contour data lon_range, lat_range, Z
<your code here>

# Create a contour plot plot from grid (lat, lon) data
figure = plt.figure()
ax = figure.add_subplot(111)
contour = ax.contour(lon_range, lat_range, Z, cmap=plt.cm.jet)

# Convert matplotlib contour to geojson
geojson = geojsoncontour.contour_to_geojson(
    contour=contour,
    ndigits=3,
    unit='m'
)

For filled contour plots (matplotlib.contourf) use contourf_to_geojson. See example_contour.py and example_contourf.py for simple but complete examples.

Show the geojson on a map

An easy way to show the generated geojson on a map is the online geojson renderer geojson.io or geojson.tools.

Style properties

Stroke color and width are set as geojson properties following https://github.com/mapbox/simplestyle-spec.

Create geojson tiles

Try geojson-vt or tippecanoe if performance is an issue and you need to tile your geojson contours.

Development

Tests

Run all tests,

python -m unittest discover

Release

Install setuptools, wheel and twine:

python -m pip install --upgrade setuptools wheel twine

Increase the version number in setup.py.

Create dist:

python setup.py sdist bdist_wheel

Upload:

twine upload dist/*

geojsoncontour's People

Contributors

bartromgens avatar demetrischr avatar nems808 avatar nzahasan avatar ucyo avatar veloman-yunkan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

geojsoncontour's Issues

min_angle_deg is none by default in contour_to_json call

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'

Package "not compatible with this Python"

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.

Feature to retain clip paths when converting to json

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

Highest matplotlib "bin" not visible

Hi there,

First of all, great work! I only have one question related to why the highest range is not visible.

Code:
geojsoncontour.contourf_to_geojson( contourf=CS, min_angle_deg=0, ndigits=99, stroke_width=0.01, fill_opacity=0.7 )

Matplotlib:
image

Geojson:
image

nice work

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)

Provide via conda-forge?

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

Invalid GeoJSON

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.

mapbox/tippecanoe#580

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?

Add option to return a Python data structure instead of a string

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?

No module named 'geojsoncontour.utilities'

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.

Suggestion of improved version of get_contourf_levels() concerning the contour labels and float formatting

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)

Geojsoncontour issue in shell script

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?

No utilities by using pip install

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.