Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rough draft implementation #1

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ docs/_build/

# PyBuilder
target/


.idea/
venv/
67 changes: 67 additions & 0 deletions coutwildrnp.geojson

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions geojseq/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from geojseq.core import FeatureStream
169 changes: 169 additions & 0 deletions geojseq/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from codecs import open as codecs_open
import json
import os
import sys


class Feature(dict):

"""Lightweight representation of a GeoJSON feature."""

def __init__(self, id, type, properties, geometry):
self._id = id
self._type = type
self._properties = properties
if isinstance(geometry, Geometry):
self._geometry = geometry
else:
self._geometry = Geometry(**geometry)

super(Feature, self).__init__(
id=self._id,
type=self._type,
properties=self._properties,
geometry=self._geometry
)

@property
def id(self):
return self._id

@property
def type(self):
return self._type

@property
def properties(self):
return self._properties

@property
def geometry(self):
return self._geometry


class Geometry(dict):

"""Lightweight representation of a GeoJSON geometry."""

def __init__(self, type, coordinates):
self._type = type
self._coordinates = coordinates
super(Geometry, self).__init__(type=self._type, coordinates=self._coordinates)

@property
def type(self):
return self._type

@property
def coordinates(self):
return self.coordinates



class FeatureStream(object):

"""
A file-like interface for operating on a stream of GeoJSON features.
"""

def __init__(self, f, mode='r', use_rs=False):

"""
Parameters
----------
f : file
A file-like object opened in a mode that compliments `mode`.
mode : str, optional
Read, write, or append data with r, w, and a.
use_rs : bool, optional
When writing, prepend each feature with a record separator (0x1E).
Ignored when reading.
kwargs : **kwargs, optional
Additional arguments for `json.loads()` or `json.dumps()`.
"""

if mode not in ('r', 'w', 'a'):
raise ValueError(
"mode string must be one of 'r', 'w', or 'a', not %s" % mode)

self._f = f
self._mode = mode
self._use_rs = use_rs

if self._mode == 'r':
self._first_line = next(self._f)
if self._first_line.startswith(u'\x1e'):
self._use_rs = True

def __repr__(self):
return "<%s FeatureStream '%s', mode '%s' at %s>" % (
self.closed and "closed" or "open",
str(self.name), self.mode, hex(id(self)))

def __iter__(self):
return self

def __next__(self):
"""Returns the next feature from the underlying file."""

if self.closed:
raise ValueError("I/O operation on closed file")
elif self.mode != 'r':
raise IOError("File not open for reading")

if self._first_line:
line = self._first_line
self._first_line = None
else:
line = next(self._f)

# Cut down on some overhead
if self._use_rs:
line = line.strip(u'\x1e')

return Feature(**json.loads(line))

next = __next__

@property
def mode(self):
"""I/O mode"""
return self._mode

@property
def name(self):
"""Path/name of file on disk."""
return getattr(self._f, 'name', '<UNKNOWN>')

def write(self, feature):
"""Write a feature to disk."""

if self.closed:
raise ValueError("I/O operation on closed file")
if self.mode not in ('a', 'w'):
raise IOError("File not open for writing")

if self._use_rs:
self._f.write(u'\u001e')

self._f.write(json.dumps(feature) + os.linesep)

def close(self):
"""Close file and sync to disk."""
self._f.close()

@property
def closed(self):
"""``False`` if data can be accessed, otherwise ``True``."""
return self._f.closed

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
self.close()

def __del__(self):
# Note: you can't count on this being called. Call close() explicitly
# or use the context manager protocol ("with").
self.__exit__(None, None, None)
Loading