HomePage: https://github.com/4teamwork/ftw.geo

Author: Lukas Graf

Download: https://pypi.python.org/packages/source/f/ftw.geo/ftw.geo-1.3.zip


This product helps integrating the ``collective.geo.*`` packages and aims to
provide some sensible defaults. Besides some integration glue it defines a new
interface ``IGeocodableLocation`` that can be used to create adapters that knows
how to represent the location of a content type with address-like fields as a
string suitable for passing to a geocoding API.


- Automatic geocoding of ``IGeoreferenceable`` content types via an
  ``IGeocodableLocation`` adapter
- Caching of geocoding responses
- Only trigger geocoding lookups if location related fields on the content item
- Facilitate doing automatic geocoding based on location fields and still allow
  for manually setting custom coordinates


Automatically geocoding your content types

In order for your content types to be automatically geocoded on ``ObjectEdited``
or ``ObjectInitialized`` events, you need to create an adapter for your content
type that implements ``IGeocodableLocation`` and knows how to build a geocodable
location string from the content type's location related fields.

In order to implement the interface you need to define a ``getLocationString``
method on your adapter that returns the complete location as a comma separated
string, with the location parts getting less specific from left to right.

For example::

    '1600 Amphitheatre Parkway, Mountain View, CA, US'
    'Engehaldestr. 53, 3012 Bern, Switzerland'

If the ``getLocationString`` method returns the empty string or ``None``, the
event handler won't attempt to do a geocode lookup, so this is the suggested way
to abort geocoding if not enough location information is available.

Example code::

    from ftw.geo.interfaces import IGeocodableLocation
    from zope.component import adapts
    from zope.interface import implements

    class MyTypeLocationAdapter(object):
        """Adapter that is able to represent the location of an MyType in
        a geocodable string form.

        def __init__(self, context):
            self.context = context

        def getLocationString(self):
            """Build a geocodable location string from the MyType's address
            related fields.
            street = self.context.getAddress()
            zip_code = self.context.getZip()
            city = self.context.getCity()
            country = self.context.getCountry()

            location = ', '.join([street, zip_code, city, country])
            return location

Register the adapter with ZCML::


Caching of geocoding responses

Responses from the geocoding API are being RAM cached. The cache key being used
is the result of the ``getLocationString`` method, which means that for every
unique location string the geocoding lookup is only done once and subsequently
fetched from the cache.

Only triggering geocoding when location fields changed

If we were to do a geocode lookup on every ``ObjectEdited`` event, any custom
coordinates that have been set would be overriden every time *any* field on
the content item is changed (even if the geocoding response itself was fetched
from the cache).

To avoid this, ``ftw.geo`` stores the result of ``getLocationString`` as an
annotation on the object and on ``ObjectEdited`` checks if the location string
(and therefore the location related fields) actually changed, and only does the
lookup when necessary. This means:

On ``ObjectInitialized`` the content type will first be geocoded initally
(unless ``getLocationString`` returned ``None`` or the empty string). If you
manually set coordinates after that through the 'Coordinates' tab provided by
``collective.geo.contentlocations`` they will be saved and overwrite the
coordinates determined previously by geocoding. After that, if you edit the
content item an