django-loci

by openwisp

openwisp / django-loci

Reusable Django app for storing geographic and indoor coordinates. Maintained by the OpenWISP Proje...

134 Stars 26 Forks Last release: 2 months ago (0.3.4) BSD 3-Clause "New" or "Revised" License 185 Commits 9 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

django-loci

.. image:: https://travis-ci.org/openwisp/django-loci.svg :target: https://travis-ci.org/openwisp/django-loci

.. image:: https://coveralls.io/repos/openwisp/django-loci/badge.svg :target: https://coveralls.io/r/openwisp/django-loci

.. image:: https://requires.io/github/openwisp/django-loci/requirements.svg?branch=master :target: https://requires.io/github/openwisp/django-loci/requirements/?branch=master :alt: Requirements Status

.. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square :target: https://gitter.im/openwisp/general

.. image:: https://badge.fury.io/py/django-loci.svg :target: http://badge.fury.io/py/django-loci

.. image:: https://pepy.tech/badge/django-loci :target: https://pepy.tech/project/django-loci :alt: downloads

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://pypi.org/project/black/ :alt: code style: black


Reusable django-app for storing GIS and indoor coordinates of objects.

.. image:: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/indoor.png :target: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/indoor.png :alt: Indoor coordinates

.. image:: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/map.png :target: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/map.png :alt: Map coordinates

.. image:: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/mobile.png :target: https://raw.githubusercontent.com/openwisp/django-loci/master/docs/mobile.png :alt: Mobile coordinates


.. contents:: Table of Contents: :backlinks: none :depth: 3


Dependencies

  • Python >= 3.6
  • GeoDjango (
    see GeoDjango Install Instructions 
    _)
  • One of the databases supported by GeoDjango

Compatibility Table

=============== ================================== django-loci Python version 0.2 2.7 or >=3.4 0.3 >=3.6 =============== ==================================

Install stable version from pypi

Install from pypi:

.. code-block:: shell

pip install django-loci

Install development version

First of all, install the dependencies of

GeoDjango 
_:
  • Geospatial libraries 
    _
  • Spatial database 
    , for development we use Spatialite, a spatial extension of
    sqlite 

Install tarball:

.. code-block:: shell

pip install https://github.com/openwisp/django-loci/tarball/master

Alternatively you can install via pip using git:

.. code-block:: shell

pip install -e git+git://github.com/openwisp/django-loci#egg=django_loci

If you want to contribute, install your cloned fork:

.. code-block:: shell

git clone [email protected]:/django-loci.git
cd django_loci
python setup.py develop

Setup (integrate in an existing django project)

First of all, set up your database engine to

one of the spatial databases suppported
by GeoDjango 
_.

Add

django_loci
and its dependencies to
INSTALLED_APPS
in the following order:

.. code-block:: python

INSTALLED_APPS = [
    # ...
    'django.contrib.gis',
    'django_loci',
    'django.contrib.admin',
    'leaflet',
    'channels'
    # ...
]

Configure

CHANNEL_LAYERS
according to your needs, a sample configuration can be:

.. code-block:: python

ASGI_APPLICATION = "django_loci.channels.routing.channel_routing"
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer",
    },
}

Now run migrations:

.. code-block:: shell

./manage.py migrate

Troubleshooting

Common issues and solutions when installing GeoDjango.

Unable to load the SpatiaLite library extension ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you get the following exception::

django.core.exceptions.ImproperlyConfigured: Unable to load the SpatiaLite library extension

You need to specify the

SPATIALITE_LIBRARY_PATH
in your
settings.py
as explained in the
django documentation regarding how to install and configure spatialte
_.

Issues with other geospatial libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Please refer to the

geodjango documentation on troubleshooting issues related to
geospatial libraries 
_.

Settings

LOCI_FLOORPLAN_STORAGE
~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+-------------------------------------------+ | type: |

str
| +--------------+-------------------------------------------+ | default: |
django_loci.storage.OverwriteStorage
| +--------------+-------------------------------------------+

The django file storage class used for uploading floorplan images.

The filestorage can be changed to a different one as long as it has an

upload_to
class method which will be passed to
FloorPlan.image.upload_to
.

To understand the details of this statement, take a look at the code of

django_loci.storage.OverwriteStorage
_.

Extending django-loci

django-loci provides a set of models and admin classes which can be imported, extended and reused by third party apps.

To extend django-loci, you MUST NOT add it to

settings.INSTALLED_APPS
, but you must create your own app (which goes into
settings.INSTALLED_APPS
), import the base classes of django-loci and add your customizations.

Extending models ~~~~~~~~~~~~~~~~

This example provides an example of how to extend the base models of django-loci by adding a relation to another django model named

Organization
.

.. code-block:: python

# models.py of your app
from django.db import models
from django_loci.base.models import (AbstractFloorPlan,
                                     AbstractLocation,
                                     AbstractObjectLocation)

the model organizations.Organization is omitted for brevity

if you are curious to see a real implementation, check out django-organizations

class OrganizationMixin(models.Model): organization = models.ForeignKey('organizations.Organization')

class Meta:
    abstract = True

class Location(OrganizationMixin, AbstractLocation): class Meta(AbstractLocation.Meta): abstract = False

def clean(self):
    # your own validation logic here...
    pass

class FloorPlan(OrganizationMixin, AbstractFloorPlan): location = models.ForeignKey(Location)

class Meta(AbstractFloorPlan.Meta):
    abstract = False

def clean(self):
    # your own validation logic here...
    pass

class ObjectLocation(OrganizationMixin, AbstractObjectLocation): location = models.ForeignKey(Location, models.PROTECT, blank=True, null=True) floorplan = models.ForeignKey(FloorPlan, models.PROTECT, blank=True, null=True)

class Meta(AbstractObjectLocation.Meta):
    abstract = False

def clean(self):
    # your own validation logic here...
    pass

Extending the admin ~~~~~~~~~~~~~~~~~~~

Following the previous

Organization
example, you can avoid duplicating the admin code by importing the base admin classes and registering your models with them.

But first you have to change a few settings in your

settings.py
, these are needed in order to load the admin templates and static files of django-loci even if it's not listed in
settings.INSTALLED_APPS
.

Add

django.forms
to
INSTALLED_APPS
, now it should look like the following:

.. code-block:: python

INSTALLED_APPS = [
    # ...
    'django.contrib.gis',
    'django_loci',
    'django.contrib.admin',
    #      ↓
    'django.forms', # 

Now add

EXTENDED_APPS
after
INSTALLED_APPS
:

.. code-block:: python

INSTALLED_APPS = [
    # ...
]

EXTENDED_APPS = ('django_loci',)

Add

openwisp_utils.staticfiles.DependencyFinder
to
STATICFILES_FINDERS
:

.. code-block:: python

STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'openwisp_utils.staticfiles.DependencyFinder',
]

Add

openwisp_utils.loaders.DependencyLoader
to
TEMPLATES
:

.. code-block:: python

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'OPTIONS': {
            'loaders': [
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
                # add the following line
                'openwisp_utils.loaders.DependencyLoader'
            ],
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    }
]

Last step, add

FORM_RENDERER
:

.. code-block:: python

FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'

Then you can go ahead and create your

admin.py
file following the example below:

.. code-block:: python

# admin.py of your app
from django.contrib import admin

from django_loci.base.admin import (AbstractFloorPlanAdmin, AbstractFloorPlanForm, AbstractFloorPlanInline, AbstractLocationAdmin, AbstractLocationForm, AbstractObjectLocationForm, AbstractObjectLocationInline) from django_loci.models import FloorPlan, Location, ObjectLocation

class FloorPlanForm(AbstractFloorPlanForm): class Meta(AbstractFloorPlanForm.Meta): model = FloorPlan

class FloorPlanAdmin(AbstractFloorPlanAdmin): form = FloorPlanForm

class LocationForm(AbstractLocationForm): class Meta(AbstractLocationForm.Meta): model = Location

class FloorPlanInline(AbstractFloorPlanInline): form = FloorPlanForm model = FloorPlan

class LocationAdmin(AbstractLocationAdmin): form = LocationForm inlines = [FloorPlanInline]

class ObjectLocationForm(AbstractObjectLocationForm): class Meta(AbstractObjectLocationForm.Meta): model = ObjectLocation

class ObjectLocationInline(AbstractObjectLocationInline): model = ObjectLocation form = ObjectLocationForm

admin.site.register(FloorPlan, FloorPlanAdmin) admin.site.register(Location, LocationAdmin)

Extending channel consumers ~~~~~~~~~~~~~~~~~~~~~~~~~~~

Extend the channel consumer of django-loci in this way:

.. code-block:: python

from django_loci.channels.base import BaseLocationBroadcast
from ..models import Location  # your own location model


class LocationBroadcast(BaseLocationBroadcast): model = Location

Extending AppConfig ~~~~~~~~~~~~~~~~~~~

You may want to reuse the

AppConfig
class of django-loci too:

.. code-block:: python

from django_loci.apps import LociConfig


class MyConfig(LociConfig): name = 'myapp' verbose_name = _('My custom app')

def __setmodels__(self):
    from .models import Location
    self.location_model = Location

Installing for development

Install sqlite:

.. code-block:: shell

sudo apt-get install sqlite3 libsqlite3-dev libsqlite3-mod-spatialite gdal-bin

Install your forked repo:

.. code-block:: shell

git clone git://github.com//django-loci
cd django-loci/
python setup.py develop

Install test requirements:

.. code-block:: shell

pip install -r requirements-test.txt

Create database:

.. code-block:: shell

cd tests/
./manage.py migrate
./manage.py createsuperuser

Launch development server and SMTP debugging server:

.. code-block:: shell

./manage.py runserver

You can access the admin interface at http://127.0.0.1:8000/admin/.

Run tests with:

.. code-block:: shell

# pytests is used to test django-channels
./runtests.py && pytest

Contributing

  1. Announce your intentions in the
    OpenWISP Mailing List 
    _
  2. Fork this repo and install it
  3. Follow
    PEP8, Style Guide for Python Code
    _
  4. Write code
  5. Write tests for your code
  6. Ensure all tests pass
  7. Ensure test coverage does not decrease
  8. Document your changes
  9. Send pull request

.. _PEP8, Style Guide for Python Code: http://www.python.org/dev/peps/pep-0008/

Changelog

See

CHANGES 
_.

License

See

LICENSE 
_.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.