Skip to content
Browse files

Refactor GeoJson class to make it more useful

Often the only thing classes inheriting from `GeoJson` had to change was the
`feature_iterator` method. In addition, these specializations were often quite
predictable. This refactoring implements the most common such cases directly
in `GeoJson.feature_iterator`, thus should lead to less app code.

closes #70
  • Loading branch information...
xrotwang committed Jul 29, 2015
1 parent 3319ac1 commit f6c679dc33ff090c735a0fbf624d27f5e4987d13
Showing with 35 additions and 24 deletions.
  1. +3 −1 clld/
  2. +13 −3 clld/tests/
  3. +15 −9 clld/web/adapters/
  4. +4 −11 clld/web/maps/
@@ -135,8 +135,10 @@ def create(request):

class IDataTable(Interface):
model = Attribute('model class of the objects listed in the table')

def get_query(request, model, **kw):
"""called to retrieve a filtered and sorted sqla query."""

class IMap(Interface):
@@ -3,11 +3,19 @@
from clld.db.models.common import Parameter, Language
from clld.tests.util import TestWithEnv
from clld.web.adapters import geojson
from clld.web.datatables.base import DataTable


class Tests(TestWithEnv):
def test_GeoJson(self):
adapter = geojson.GeoJson(None)
self.assertEquals(len(list(adapter.feature_iterator(None, None))), 0)
self.assertEquals(len(list(adapter.feature_iterator(Language(), None))), 1)
len(list(adapter.feature_iterator(Mock(languages=[Language()]), None))), 1)

def test_GeoJsonParameter(self):
adapter = geojson.GeoJsonParameter(None)
@@ -28,13 +36,15 @@ def test_GeoJsonParameterFlatProperties(self):
'{' in adapter.render(Parameter.get('no-domain'), self.env['request']))

def test_GeoJsonLanguages(self):
class MockLanguages(Mock):
class MockLanguages(DataTable):
def get_query(self, *args, **kw):
return [Language.first()]

adapter = geojson.GeoJsonLanguages(None)
'{' in adapter.render(MockLanguages(), self.env['request']))
MockLanguages(self.env['request'], Language), self.env['request']))

def test_get_lonlat(self):
@@ -13,7 +13,7 @@
from clld.web.adapters.base import Renderable
from clld import interfaces
from clld.db.meta import DBSession
from clld.db.models.common import ValueSet, Value
from clld.db.models.common import ValueSet, Value, Language

@@ -116,7 +116,20 @@ def featurecollection_properties(self, ctx, req):
return {}

def feature_iterator(self, ctx, req):
return iter([]) # pragma: no cover
"""Yield objects which will be represented as GeoJSON features.
Since the only objects we have geographic info about are Languages, we try to
detect (collections of) Languages associated with ctx.
:return: generator object.
if interfaces.IDataTable.providedBy(ctx) and ctx.model == Language:
return ctx.get_query(limit=5000)
if hasattr(ctx, 'languages'):
return ctx.languages
if interfaces.ILanguage.providedBy(ctx):
return [ctx]
return []

def feature_properties(self, ctx, req, feature):
"""override to add properties."""
@@ -197,9 +210,6 @@ class GeoJsonCombinationDomainElement(GeoJson):

"""GeoJSON adapter for a domain element of a combination of parameters."""

def feature_iterator(self, ctx, req):
return ctx.languages

def feature_properties(self, ctx, req, language):
return {
'icon': ctx.icon.url(req) if ctx.icon else '',
@@ -226,8 +236,4 @@ def render(self, ctx, req, dump=True):

class GeoJsonLanguages(GeoJson):

"""Render a collection of languages as geojson feature collection."""

def feature_iterator(self, ctx, req):
return ctx.get_query(limit=5000)
@@ -7,9 +7,7 @@
from clld.web.util import helpers
from clld.web.util.htmllib import HTML
from clld.web.util.component import Component
from clld.web.adapters.geojson import (
GeoJson, GeoJsonLanguages, GeoJsonCombinationDomainElement, get_lonlat,
from clld.web.adapters.geojson import GeoJson, GeoJsonCombinationDomainElement, get_lonlat
from clld.util import cached_property

@@ -335,11 +333,6 @@ def get_options(self):
return {'icon_size': 25, 'hash': True}

class _GeoJson(GeoJsonLanguages):
def feature_iterator(self, ctx, req):
return [ctx]

class LanguageMap(Map):

"""Map showing a single language."""
@@ -348,18 +341,18 @@ def get_layers(self):
yield Layer(,,
_GeoJson(self.ctx).render(self.ctx, self.req, dump=False))
GeoJson(self.ctx).render(self.ctx, self.req, dump=False))

def get_default_options(self):
return {
'center': list(reversed(get_lonlat(self.ctx))),
'center': list(reversed(get_lonlat(self.ctx) or [0, 0])),
'zoom': 3,
'no_popup': True,
'no_link': True,
'sidebar': True}

class GeoJsonSelectedLanguages(GeoJsonLanguages):
class GeoJsonSelectedLanguages(GeoJson):

"""Represents the geo-data of an iterable selection of languages.

0 comments on commit f6c679d

Please sign in to comment.
You can’t perform that action at this time.