Code Monkey home page Code Monkey logo

geojson's Introduction

geojson

GitHub Actions Codecov Jazzband PyPI

This Python library contains:

Table of Contents

Installation

geojson is compatible with Python 3.7 - 3.12. The recommended way to install is via pip:

pip install geojson

GeoJSON Objects

This library implements all the GeoJSON Objects described in The GeoJSON Format Specification.

All object keys can also be used as attributes.

The objects contained in GeometryCollection and FeatureCollection can be indexed directly.

Point

>>> from geojson import Point

>>> Point((-115.81, 37.24))  # doctest: +ELLIPSIS
{"coordinates": [-115.8..., 37.2...], "type": "Point"}

Visualize the result of the example above here. General information about Point can be found in Section 3.1.2 and Appendix A: Points within The GeoJSON Format Specification.

MultiPoint

>>> from geojson import MultiPoint

>>> MultiPoint([(-155.52, 19.61), (-156.22, 20.74), (-157.97, 21.46)])  # doctest: +ELLIPSIS
{"coordinates": [[-155.5..., 19.6...], [-156.2..., 20.7...], [-157.9..., 21.4...]], "type": "MultiPoint"}

Visualize the result of the example above here. General information about MultiPoint can be found in Section 3.1.3 and Appendix A: MultiPoints within The GeoJSON Format Specification.

LineString

>>> from geojson import LineString

>>> LineString([(8.919, 44.4074), (8.923, 44.4075)])  # doctest: +ELLIPSIS
{"coordinates": [[8.91..., 44.407...], [8.92..., 44.407...]], "type": "LineString"}

Visualize the result of the example above here. General information about LineString can be found in Section 3.1.4 and Appendix A: LineStrings within The GeoJSON Format Specification.

MultiLineString

>>> from geojson import MultiLineString

>>> MultiLineString([
...     [(3.75, 9.25), (-130.95, 1.52)],
...     [(23.15, -34.25), (-1.35, -4.65), (3.45, 77.95)]
... ])  # doctest: +ELLIPSIS
{"coordinates": [[[3.7..., 9.2...], [-130.9..., 1.52...]], [[23.1..., -34.2...], [-1.3..., -4.6...], [3.4..., 77.9...]]], "type": "MultiLineString"}

Visualize the result of the example above here. General information about MultiLineString can be found in Section 3.1.5 and Appendix A: MultiLineStrings within The GeoJSON Format Specification.

Polygon

>>> from geojson import Polygon

>>> # no hole within polygon
>>> Polygon([[(2.38, 57.322), (-120.43, 19.15), (23.194, -20.28), (2.38, 57.322)]])  # doctest: +ELLIPSIS
{"coordinates": [[[2.3..., 57.32...], [-120.4..., 19.1...], [23.19..., -20.2...]]], "type": "Polygon"}

>>> # hole within polygon
>>> Polygon([
...     [(2.38, 57.322), (-120.43, 19.15), (23.194, -20.28), (2.38, 57.322)],
...     [(-5.21, 23.51), (15.21, -10.81), (-20.51, 1.51), (-5.21, 23.51)]
... ])  # doctest: +ELLIPSIS
{"coordinates": [[[2.3..., 57.32...], [-120.4..., 19.1...], [23.19..., -20.2...]], [[-5.2..., 23.5...], [15.2..., -10.8...], [-20.5..., 1.5...], [-5.2..., 23.5...]]], "type": "Polygon"}

Visualize the results of the example above here. General information about Polygon can be found in Section 3.1.6 and Appendix A: Polygons within The GeoJSON Format Specification.

MultiPolygon

>>> from geojson import MultiPolygon

>>> MultiPolygon([
...     ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],),
...     ([(23.18, -34.29), (-1.31, -4.61), (3.41, 77.91), (23.18, -34.29)],)
... ])  # doctest: +ELLIPSIS
{"coordinates": [[[[3.7..., 9.2...], [-130.9..., 1.5...], [35.1..., 72.23...]]], [[[23.1..., -34.2...], [-1.3..., -4.6...], [3.4..., 77.9...]]]], "type": "MultiPolygon"}

Visualize the result of the example above here. General information about MultiPolygon can be found in Section 3.1.7 and Appendix A: MultiPolygons within The GeoJSON Format Specification.

GeometryCollection

>>> from geojson import GeometryCollection, Point, LineString

>>> my_point = Point((23.532, -63.12))

>>> my_line = LineString([(-152.62, 51.21), (5.21, 10.69)])

>>> geo_collection = GeometryCollection([my_point, my_line])

>>> geo_collection  # doctest: +ELLIPSIS
{"geometries": [{"coordinates": [23.53..., -63.1...], "type": "Point"}, {"coordinates": [[-152.6..., 51.2...], [5.2..., 10.6...]], "type": "LineString"}], "type": "GeometryCollection"}

>>> geo_collection[1]
{"coordinates": [[-152.62, 51.21], [5.21, 10.69]], "type": "LineString"}

>>> geo_collection[0] == geo_collection.geometries[0]
True

Visualize the result of the example above here. General information about GeometryCollection can be found in Section 3.1.8 and Appendix A: GeometryCollections within The GeoJSON Format Specification.

Feature

>>> from geojson import Feature, Point

>>> my_point = Point((-3.68, 40.41))

>>> Feature(geometry=my_point)  # doctest: +ELLIPSIS
{"geometry": {"coordinates": [-3.68..., 40.4...], "type": "Point"}, "properties": {}, "type": "Feature"}

>>> Feature(geometry=my_point, properties={"country": "Spain"})  # doctest: +ELLIPSIS
{"geometry": {"coordinates": [-3.68..., 40.4...], "type": "Point"}, "properties": {"country": "Spain"}, "type": "Feature"}

>>> Feature(geometry=my_point, id=27)  # doctest: +ELLIPSIS
{"geometry": {"coordinates": [-3.68..., 40.4...], "type": "Point"}, "id": 27, "properties": {}, "type": "Feature"}

Visualize the results of the examples above here. General information about Feature can be found in Section 3.2 within The GeoJSON Format Specification.

FeatureCollection

>>> from geojson import Feature, Point, FeatureCollection

>>> my_feature = Feature(geometry=Point((1.6432, -19.123)))

>>> my_other_feature = Feature(geometry=Point((-80.234, -22.532)))

>>> feature_collection = FeatureCollection([my_feature, my_other_feature])

>>> feature_collection # doctest: +ELLIPSIS
{"features": [{"geometry": {"coordinates": [1.643..., -19.12...], "type": "Point"}, "properties": {}, "type": "Feature"}, {"geometry": {"coordinates": [-80.23..., -22.53...], "type": "Point"}, "properties": {}, "type": "Feature"}], "type": "FeatureCollection"}

>>> feature_collection.errors()
[]

>>> (feature_collection[0] == feature_collection['features'][0], feature_collection[1] == my_other_feature)
(True, True)

Visualize the result of the example above here. General information about FeatureCollection can be found in Section 3.3 within The GeoJSON Format Specification.

GeoJSON encoding/decoding

All of the GeoJSON Objects implemented in this library can be encoded and decoded into raw GeoJSON with the geojson.dump, geojson.dumps, geojson.load, and geojson.loads functions. Note that each of these functions is a wrapper around the core json function with the same name, and will pass through any additional arguments. This allows you to control the JSON formatting or parsing behavior with the underlying core json functions.

>>> import geojson

>>> my_point = geojson.Point((43.24, -1.532))

>>> my_point  # doctest: +ELLIPSIS
{"coordinates": [43.2..., -1.53...], "type": "Point"}

>>> dump = geojson.dumps(my_point, sort_keys=True)

>>> dump  # doctest: +ELLIPSIS
'{"coordinates": [43.2..., -1.53...], "type": "Point"}'

>>> geojson.loads(dump)  # doctest: +ELLIPSIS
{"coordinates": [43.2..., -1.53...], "type": "Point"}

Custom classes

This encoding/decoding functionality shown in the previous can be extended to custom classes using the interface described by the __geo_interface__ Specification.

>>> import geojson

>>> class MyPoint():
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...
...     @property
...     def __geo_interface__(self):
...         return {'type': 'Point', 'coordinates': (self.x, self.y)}

>>> point_instance = MyPoint(52.235, -19.234)

>>> geojson.dumps(point_instance, sort_keys=True)  # doctest: +ELLIPSIS
'{"coordinates": [52.23..., -19.23...], "type": "Point"}'

Default and custom precision

GeoJSON Object-based classes in this package have an additional precision attribute which rounds off coordinates to 6 decimal places (roughly 0.1 meters) by default and can be customized per object instance.

>>> from geojson import Point

>>> Point((-115.123412341234, 37.123412341234))  # rounded to 6 decimal places by default
{"coordinates": [-115.123412, 37.123412], "type": "Point"}

>>> Point((-115.12341234, 37.12341234), precision=8)  # rounded to 8 decimal places
{"coordinates": [-115.12341234, 37.12341234], "type": "Point"}

Precision can be set at the package level by setting geojson.geometry.DEFAULT_PRECISION

>>> import geojson

>>> geojson.geometry.DEFAULT_PRECISION = 5

>>> from geojson import Point

>>> Point((-115.12341234, 37.12341234))  # rounded to 8 decimal places
{"coordinates": [-115.12341, 37.12341], "type": "Point"}

After setting the DEFAULT_PRECISION, coordinates will be rounded off to that precision with geojson.load or geojson.loads. Following one of those with geojson.dump is a quick and easy way to scale down the precision of excessively precise, arbitrarily-sized GeoJSON data.

Helpful utilities

coords

geojson.utils.coords yields all coordinate tuples from a geometry or feature object.

>>> import geojson

>>> my_line = LineString([(-152.62, 51.21), (5.21, 10.69)])

>>> my_feature = geojson.Feature(geometry=my_line)

>>> list(geojson.utils.coords(my_feature))  # doctest: +ELLIPSIS
[(-152.62..., 51.21...), (5.21..., 10.69...)]

map_coords

geojson.utils.map_coords maps a function over all coordinate values and returns a geometry of the same type. Useful for scaling a geometry.

>>> import geojson

>>> new_point = geojson.utils.map_coords(lambda x: x/2, geojson.Point((-115.81, 37.24)))

>>> geojson.dumps(new_point, sort_keys=True)  # doctest: +ELLIPSIS
'{"coordinates": [-57.905..., 18.62...], "type": "Point"}'

map_tuples

geojson.utils.map_tuples maps a function over all coordinates and returns a geometry of the same type. Useful for changing coordinate order or applying coordinate transforms.

>>> import geojson

>>> new_point = geojson.utils.map_tuples(lambda c: (c[1], c[0]), geojson.Point((-115.81, 37.24)))

>>> geojson.dumps(new_point, sort_keys=True)  # doctest: +ELLIPSIS
'{"coordinates": [37.24..., -115.81], "type": "Point"}'

map_geometries

geojson.utils.map_geometries maps a function over each geometry in the input.

>>> import geojson

>>> new_point = geojson.utils.map_geometries(lambda g: geojson.MultiPoint([g["coordinates"]]), geojson.GeometryCollection([geojson.Point((-115.81, 37.24))]))

>>> geojson.dumps(new_point, sort_keys=True)
'{"geometries": [{"coordinates": [[-115.81, 37.24]], "type": "MultiPoint"}], "type": "GeometryCollection"}'

validation

is_valid property provides simple validation of GeoJSON objects.

>>> import geojson

>>> obj = geojson.Point((-3.68,40.41,25.14,10.34))
>>> obj.is_valid
False

errors method provides collection of errors when validation GeoJSON objects.

>>> import geojson

>>> obj = geojson.Point((-3.68,40.41,25.14,10.34))
>>> obj.errors()
'a position must have exactly 2 or 3 values'

generate_random

geojson.utils.generate_random yields a geometry type with random data

>>> import geojson

>>> geojson.utils.generate_random("LineString")  # doctest: +ELLIPSIS
{"coordinates": [...], "type": "LineString"}

>>> geojson.utils.generate_random("Polygon")  # doctest: +ELLIPSIS
{"coordinates": [...], "type": "Polygon"}

Development

To build this project, run python setup.py build. To run the unit tests, run python -m pip install tox && tox. To run the style checks, run flake8 (install flake8 if needed).

Credits

geojson's People

Contributors

amotl avatar auvipy avatar bennlich avatar bors[bot] avatar bryik avatar chrisvoncsefalvay avatar frewsxcv avatar groteworld avatar harmon758 avatar hrfuller avatar hugovk avatar hyperknot avatar janlikar avatar jazzband-bot avatar jesseweinstein avatar jezdez avatar jlaxson avatar joker234 avatar lafrech avatar noharamasato avatar noirbizarre avatar pre-commit-ci[bot] avatar rayrrr avatar rowanwins avatar rumpelstinsk avatar saromanov avatar steko avatar tiktaalik-dev avatar trcmohitmandokhot avatar udos 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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

geojson's Issues

consider PEP 257 compliance

Some people don't like it (or don't care) and some do, so I wanted your opinion on being PEP 257 complaint before taking the time to write all the doc strings.

Incorrect Expected Type for Classes with Coordinate Lists

I believe the type hinting is incorrect for classes where coordinates are defined by multiple points.

The documentation indicates that the following code should work.

def geojson_serialize(self):
    return Feature(geometry=LineString(coordinates=[(self.city.longitude, self.city.latitude),
                                                    (self.host.city.longitude, self.host.city.latitude)]),
                   properties={"attack_type": self.attack_type.name,
...

When run, it does seem to work as intended. However, it produces a weak warning:
Expected type 'tuple', got 'List[Tuple[Any,Any]]' instead.

Because LineString inherits from MultiPoint which inherits from Geometry, it uses the following constructor.

    def __init__(self, coordinates=None, crs=None, validate=False, **extra):
        """
        Initialises a Geometry object.

        :param coordinates: Coordinates of the Geometry object.
        :type coordinates: tuple
        :param crs: CRS
        :type crs: CRS object
        """

which is incorrect for any MultiPoint coordinates.

Would the use of typing.Union be an appropriate fix here?

I'm willing to submit a PR.

Polygon Encoding issue

Hi Geojson Team, thanks for this great library.

I'm having an issue when putting Polygon Features together into a FeatureCollection. Here's an example.

import geojson
from geojson import Point, Polygon, Feature, FeatureCollection

a = Polygon([(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15)])
b = Polygon( [(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15)])
c = Feature(geometry=a)
d = Feature(geometry=b)
e = FeatureCollection([c,d])
print geojson.dumps(e)

{"type": "FeatureCollection", "features": [{"geometry": {"type": "Polygon", "coordinates": [[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15]]}, "type": "Feature", "id": null, "properties": {}}, {"geometry": {"type": "Polygon", "coordinates": [[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15]]}, "type": "Feature", "id": null, "properties": {}}]}'

Notice in the output that there are two brackets in the polygon coordinates. The GeoJson spec for Polygon has three brackets whether the Polyogn has holes or not where the expected output would be as follows:

{"type": "FeatureCollection", "features": [{"geometry": {"type": "Polygon", "coordinates": [[[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15]]]}, "type": "Feature", "id": null, "properties": {}}, {"geometry": {"type": "Polygon", "coordinates": [][2.38, 57.322], [23.194, -20.28], [-120.43, 19.15][]}, "type": "Feature", "id": null, "properties": {}}]}'

using a valid geojson dictionary in a contructor

There are cases where I have a dictionary that was decoded from valid JSON, and the JSON happened to be a GeoJSON object. It would be convenient to have a "load()" function that turns those in to instances of the geojson classes.

In other words,
If I have this:

     # from some other source
    valid_geojson_decode = dict(type="Polygon", coordinates=....}

Now I have to do this:

     geojson.loads(json.dumps(valid_geojson_decode ))

Would like to do this:

     geojson.from_dict(valid_geojson_decode )

Computing Bounding Boxes

Is there a way to compute bounding boxes for Geometries and/or Features?
If not, would you consider merging a potential PR adding this functionality? (in utils or even as object property, we can discuss that).

Add option to allow specifying floating point precision on geojson.dump/geojson.dumps

Just received an email with this request:

I would like to suggest that you provide a means to specify the
precision of floats that the module outputs, perhaps a format string as
an argument to dump()/dumps().

A typical coordinate coming from geojson looks like: [-74.4995503571598,
39.42247551734061]. That's 14 decimal places in the fraction, far, far
more than I'll ever need considering that 8 places gives 1.1mm
precision. I would like to reduce the file size by limiting the
precision to a sensible value.

Allow constructing objects like Feature(feature)

In Python most object support creating a new object from an old one, (AFAIK by doing a copy operation), by constructing it in the form of:

a = dict(...)
b = dict(a)

In geojson it is not supported. If I try to do:

f = Feature(...)
f2 = Feature(f)

It creates a broken object like:

{"geometry": null, "id": {"geometry": null, "properties": {}, "type": "Feature"}, "properties": {}, "type": "Feature"}

This would be especially important as geojson is inheriting from dict, so ideally it should behave in a similar way.

loads may fail when features include properties named "type"

loads(json, object_hook=GeoJSON.to_instance) fails if json includes features with properties named "type" that reference null, strings with non-ascii characters, "Feature" or "FeatureCollection". See the examples below.

With "type":null:

>>> from geojson import loads
>>> from geojson import GeoJSON
>>> loads('{"type": "Feature", "geometry": null, "id": 1, "properties": {"type": null}}', object_hook=GeoJSON.to_instance)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/codec.py",
line 46, in loads
   return simplejson.loads(s, cls=cls, object_hook=object_hook, **kwargs)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/__init__.py",
line 402, in loads
   return cls(encoding=encoding, **kw).decode(s)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/decoder.py",
line 402, in decode
   obj, end = self.raw_decode(s, idx=_w(s, 0).end())
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/decoder.py",
line 418, in raw_decode
   obj, end = self.scan_once(s, idx)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/base.py",
line 55, in to_instance
   geojson_factory = getattr(geojson.factory, type_)
TypeError: getattr(): attribute name must be string

With non-ascii chars:

>>> from geojson import loads
>>> from geojson import GeoJSON
>>> loads('{"type": "Feature", "geometry": null, "id": 1, "properties": {"type": "é"}}', object_hook=GeoJSON.to_instance)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/codec.py",
line 46, in loads
   return simplejson.loads(s, cls=cls, object_hook=object_hook, **kwargs)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/__init__.py",
line 402, in loads
   return cls(encoding=encoding, **kw).decode(s)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/decoder.py",
line 402, in decode
   obj, end = self.raw_decode(s, idx=_w(s, 0).end())
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/simplejson-2.1.6-py2.6-linux-i686.egg/simplejson/decoder.py",
line 418, in raw_decode
   obj, end = self.scan_once(s, idx)
 File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/base.py",
line 55, in to_instance
   geojson_factory = getattr(geojson.factory, type_)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in
position 0: ordinal not in range(128)

With "type":"Feature":

>>> from geojson import loads
>>> from geojson import GeoJSON
>>> loads('{"type": "Feature", "geometry": null, "id": 1, "properties": {"type": "Feature"}}', object_hook=GeoJSON.to_instance)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/base.py", line 16, in __repr__
    for (key, value) in self
  File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/base.py", line 15, in <genexpr>
    params = ", ".join("%s=%s" % (key, value) 
  File "/home/elemoine/.virtualenvs/papyrus/lib/python2.6/site-packages/geojson-1.0.1-py2.6.egg/geojson/base.py", line 29, in iter_geo_interface
    key, value = iter_geo_interface(value)
ValueError: too many values to unpack

geojson.load() chokes on non-ascii property keys

The following input data causes geojson.load() to fail with:

  File "/usr/local/lib/python2.7/site-packages/geojson/base.py", line 47, in <genexpr>
    d = dict((str(k), mapping[k]) for k in mapping)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 9: ordinal not in range(128)

data.geojson:

{
  "features": [
    {
      "geometry": {
        "coordinates": [
          [
            700251.2679938598, 
            6154023.085332587
          ], 
      [
            700516.3475396622, 
            6155237.880750462
          ]
        ], 
        "type": "LineString"
      }, 
      "id": 3498, 
      "properties": {
        "osm$highway": "motorway", 
        "osm$stamv\u00e4g": "yes", 
        "osm$way_id": 2036271 
      }, 
      "type": "Feature"
    }
  ],
"type": "FeatureCollection"
}

bug.py:

import geojson
fname = 'data.geojson'
f = open(fname,'r')
geo = geojson.load(f)

The following fix works for me:
In site-packages/geojson/base.py, in the to_instance method of GeoJson replace:

       d = dict((str(k), mapping[k]) for k in mapping)

with:

       #d = dict((str(k), mapping[k]) for k in mapping)
        d = {}
        for k in mapping:
            try:
                str_key = str(k)
            except (UnicodeEncodeError):
                str_key = unicode(k)
            d[str_key] = mapping[k]

geojson.Poylgon is not producing 100% correct output

I have a list of (lon, lat) values, which I'm loading in via

foo = geojson.Polygon(points)

When I use geojson.dumps, I get something like:

{"type": "Polygon", "coordinates": [[152.59136962890625, -28.313671112060547], [152.63580322265625, -28.355209350585938], [152.82142639160156, -28.509435653686523], [153.00906372070312, -28.665510177612305], [153.18821716308594, -28.82417106628418], [152.59136962890625, -28.313671112060547]]}

I have removed a bunch of intermediate points from this snippet.

I then used http://geojsonlint.com/ to validate the output. I got

"Failed to validate field 'coordinates' list schema: Length of value [152.59136962890625, -28.313671112060547] for list item must be greater than or equal to 4"

The issue appears to be some missing square brackets. If I put the coordinate list inside another list, i.e. open with [[[ and close with ]]], then it seems to work fine.

It seems like this might be an error with geojson rather than something I'm doing wrong. I then tried calling geojson with the same trick, wrapping my list of points inside another list. This seemed to work pretty well. So I've got a workaround, but it seems like maybe geojson should or could notice the issue and report the exception.

setuptools is listed as runtime dependency

The install_requires argument in setup.py lists setuptools as dependency. However, only runtime dependencies should be included there.

However, having setuptools as dependency can be a problem, because installing this package then also installs setuptools and the packages setuptools depends on, although they are not required. This situation (installing more packages than required) increases the chance of version conflicts.

I therefore suggest to remove this dependency altogether.

Counter-clockwise polygons and dateline-crossing bounding boxen

@frewsxcv 2 draft-geojson PRs have possible consequences for python-geojson: geojson/draft-geojson#42, geojson/draft-geojson#42. The first recommends counter-clockwise winding for exterior rings of polygons (aka right-hand rule) and the second makes a recommendation that bounding boxes be [west, south, east, north] instead of [min(long), min(lat), max(long), max(lat)] to make dealing with the dateline more sane. Any thoughts or comments?

A function to wind/rewind polygons accordingly could be a good feature for python-geojson.

Numpy scalars don't work in feature properties

I noticed that the json library in Python 3.3 (and likely other versions) isn't playing nice with
numpy scalars. Given the GIS ecosystem's affinity for numpy, I'm curious if:

  1. if this has come up before and
  2. it's something we could shield users from (via some form of sanitizer)

Consider the following:

import numpy
import geojson

pnt = geojson.Point(coordinates=(15.7, 12.2))
props_np = {'area': numpy.float64(20.5), 'sections': numpy.int64(100), 'name': 'Test Area'}
props_std = {'area': 20.5, 'sections': 100, 'name': 'Test Area'}

feat_np = geojson.Feature(id=0, geometry=pnt, properties=props_np)
feat_std = geojson.Feature(id=0, geometry=pnt, properties=props_std)

print(feat_std) # works fine
print(feat_np) 
{"geometry": {"coordinates": [15.7, 12.2], "type": "Point"}, "id": 0, "properties": {"area": 20.5, "name": "Test Area", "sections": 100}, "type": "Feature"}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-beb75a42ffbc> in <module>()
     17 
     18 print(feat_std)
---> 19 print(feat_np)
------------------------
... [yada yada]
C:\Miniconda3\envs\pnr\lib\json\encoder.py in encode(self, o)
    190         # exceptions aren't as detailed.  The list call should be roughly
    191         # equivalent to the PySequence_Fast that ''.join() would do.
--> 192         chunks = self.iterencode(o, _one_shot=True)
    193         if not isinstance(chunks, (list, tuple)):
    194             chunks = list(chunks)

C:\Miniconda3\envs\pnr\lib\json\encoder.py in iterencode(self, o, _one_shot)
    248                 self.key_separator, self.item_separator, self.sort_keys,
    249                 self.skipkeys, _one_shot)
--> 250         return _iterencode(o, 0)
    251 
    252 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,

C:\Miniconda3\envs\pnr\lib\json\encoder.py in default(self, o)
    171 
    172         """
--> 173         raise TypeError(repr(o) + " is not JSON serializable")
    174 
    175     def encode(self, o):

TypeError: 100 is not JSON serializable

Unicode escape

Is there equivalent to ensure_ascii=False in this module? It converts utf-8 text to escape sequence.

Source distribution

It would be nice to get a source distribution for version 2.0.0 in either PyPI or GitHub. As of now there is only wheels on PyPI.

Improve the documentation

Right now the documentation is relatively unorganized and not entirely informative. Possible suggestions for improvement include conversion to markdown (for use on GitHub) or rst (for use on readthedocs). Additionally, a complete page documenting the API would greatly benefit new users. As per this comment I'd be willing to take this task

Generate random geojson

Hey everyone,

Thanks for a great python library. Just wondering if you'd be interested in extending it to support generating random features? eg create me a linestring.

>>> from geojson import LineString

>>> Random_LineString() 
{"coordinates": [[8.91..., 44.407...], [8.92..., 44.407...]], "type": "LineString"}

I'd be happy to attempt to make a start even though I dont entriely know what Im getting myself in for!

Cheers,
Rowan

Error when importing native Python JSON

I get the following error when using an app that depens on GeoJSON:

…
File '/srv/sites/tasks.openstreetmap.se/env/local/lib/python2.7/site-packages/papyrus-0.7-py2.7.egg/papyrus/renderers.py', line 73 in _render
  ret = geojson.dumps(value, cls=Encoder, use_decimal=True)
File '/srv/sites/tasks.openstreetmap.se/env/local/lib/python2.7/site-packages/geojson/codec.py', line 30 in dumps
  cls=cls, allow_nan=allow_nan, **kwargs)
File '/usr/lib/python2.7/json/__init__.py', line 238 in dumps
  **kw).encode(obj)
TypeError: __init__() got an unexpected keyword argument 'use_decimal'

Changing "import json" to "import simplejson as json" on the first line in geojson/codec.py fixes the problem.

Unable to validate FeatureCollection

Hello,

I was trying to see whether the is_valid would work for feature collection. I tested using the Example GeoJSON Feature Collection.

import geojson
test_fc = { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, "properties": {"prop0": "value0"} }, { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] ] }, "properties": { "prop0": "value0", "prop1": 0.0 } }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ] }, "properties": { "prop0": "value0", "prop1": {"this": "that"} } } ] }

geojson.is_valid(geojson.FeatureCollection(test_fc)
('message': '', 'valid': 'yes'}

However it seems that it returns valid for just about anything

test_fc = 'BeepBoop'
geojson.is_valid(geojson.FeatureCollection(test_fc)
('message': '', 'valid': 'yes'}

Is there a different way to go about this or is FeatureCollection validation not supported at this moment?

Included JSON encoder can't handle Decimal

Example

from geojson import Point
print(Point((Decimal('0'), Decimal('0'))))
TypeError: Decimal('0') is not JSON serializable

Expected behavior:

from geojson import Point
print(Point((Decimal('0'), Decimal('0'))))
{"coordinates": ["0", "0"], "type": "Point"}

Workaround

json.dumps(Point((Decimal('0'), Decimal('0'))), cls=AnotherJSONEncoder)

loads() returns inconsistent types

python -c "import geojson; geojson.loads('{}').is_valid"
AttributeError: 'dict' object has no attribute 'is_valid'

If loads(x) succeeds it returns a geojson.feature.____ object, otherwise it returns the result of json.loads(x).

I recommend it always return a feature object.

map_coords for tuples

I think it would be very useful to have a function that would work similar to the geojson.utils.map_coords, but would call the mapping function with tuples of coordinates.

This would, for instance, enable writing simple converters between projections.

Please update PyPi

Please release a new version of geojson. The one on PyPi is missing a lot of the improvements you have on your tree.

Thanks a lot!
-- Simon

CRS on a FeatureCollection is not read or written

There is existing code inside python-geojson in order to handle a CRS attached to an individual point.

There is no way to access or write the CRS for an entire FeatureCollection. This information is presently dropped when read, and it is not possible to write this back to a file.

Too strict nose dependency

Dependency setup_requires=['nose==1.3.0', ] in setup.py leads to the fact that the package can not be installed in case environment has newer version of nose.

For example: package foo have install_requires=['nose', 'geojson']. You can't install this package using pip install foo, because pip firstly installs nose==1.3.1 from pypi and after that can't install geojson within version conflict.

To resolve this issue I suggest:

  1. Remove setup_requires=['nose==1.3.0', ] at all, because nose required only for running tests. Tests can be started using python setup.py nosetests.
  2. In tests_require change nose==1.3.0 to nose>=1.3 and coverage==3.6 to coverage>=3.6.

geojson.map_coords.__doc___ confusing

I'm doing this test case:
print(geojson.map_coords.__doc__)

Returns the coordinates from a Geometry after applying the provided
function to the tuples.
:param func: Function to apply to tuples
:type func: function
:param obj: A geometry or feature to extract the coordinates from.
:type obj: Point, LineString, MultiPoint, MultiLineString, Polygon,
MultiPolygon
:return: The result of applying the function to each coordinate array.
:rtype: list
:raises ValueError: if the provided object is not a Geometry.
     for item in geoj:
         print(item)
         #geoj_proj = geojson.utils.map_coords(lambda x: conversions.LFtoLongLat(x), item)
         geoj_proj = geojson.utils.map_coords(lambda x: print(x), item)
         break

{"coordinates": [[4493.178634548326, 3454.400914449211], [4483.730689118465, 3449.566045195004], [4500, 3452.474407915964]], "type": "LineString"}
4493.178634548326
3454.400914449211
4483.730689118465
3449.566045195004
4500
3452.474407915964

I expected print(x) to bu a tuple? As the doc says 'after applying the provided function to the tuples. Am I missing something? My conversions.LFtoLongLat expect a tuple, as I'm doing an affine transformation.

Release 1.3.1 missing in source repository

Hi,

I just noticed that release 1.3.1 has been pushed to PyPI, however the source published here claims to still be 1.3.0. Could you please push the missing changes and the tag?

Regards,
Matthias

Circular reference on module import

Hello there,

I'm experiencing an endless sequence of Import Errors because there's several circular references between base.py, codec.py, factory.py and mapping.py.

I'm at the end of my tether trying to make them work as they are so I reckon I'll just merge those files into one.

I'd recommend for you to rearrange the classes in a way that prevent this from happening.

Anyway, thanks for making available this library! :-)

Cheers,
Rodrigo Gambra

Point with 3 coordinates or more does not pass validation while valid as per the GeoJSON specs

Please excuse me if I'm mistaken, but as I was reading the GeoJSON specs, I came accross a paragraph mentioning positions with more than two elements:

A position is represented by an array of numbers. There must be at least two elements, and may be more. The order of elements must follow x, y, z order (easting, northing, altitude for coordinates in a projected coordinate reference system, or longitude, latitude, altitude for coordinates in a geographic coordinate reference system). Any number of additional elements are allowed -- interpretation and meaning of additional elements is beyond the scope of this specification.

In python-geojson's validator, this is explicitly forbidden:

code:

    if isinstance(obj, geojson.Point):
        if len(obj['coordinates']) != 2:
            return output('the "coordinates" member must be a single position')

tests:

    def test_invalid_point(self):
        point = geojson.Point((10, 20, 30))
        self.assertEqual(is_valid(point)['valid'], NO)

If my understanding is correct, the validator should allow any number of elements greater than of equal to two.

The same change was made in MongoDB:

tests: include data.geojson into the PyPI distfile

I realise that data.geojson seems not to be part of the PyPI distfile. This results in one test to fail, so I'd propose to include this file as well. I guess the fix is quick and just about modifying the MANIFEST.in. I also would propose to bug fix release after fixing this.

sudo nosetests .
..................E........................
======================================================================
ERROR: test_unicode_properties (tests.test_features.FeaturesTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/local/var/macports/build/_Users_petr_sandbox_SVN-repos_macports_dports_python_py-geojson/py27-geojson/work/geojson-1.2.1/tests/test_features.py", line 37, in test_unicode_properties
    with open("tests/data.geojson") as file_:
IOError: [Errno 2] No such file or directory: 'tests/data.geojson'

----------------------------------------------------------------------
Ran 43 tests in 0.035s

FAILED (errors=1)

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.