Skip to main content

Reusable django-app for outdoor and indoor mapping

Project description

CI build status https://coveralls.io/repos/openwisp/django-loci/badge.svg Dependency monitoring https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square https://badge.fury.io/py/django-loci.svg downloads code style: black

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

Indoor coordinates Map coordinates Mobile coordinates

Dependencies

Compatibility Table

django-loci

Python version

0.2

2.7 or >=3.4

0.3 - 0.4

>=3.6

1.0

>=3.7

1.1

>=3.8

dev

>=3.8

Install stable version from pypi

Install from pypi:

pip install django-loci

Install development version

First of all, install the dependencies of GeoDjango:

Install tarball:

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

Alternatively you can install via pip using git:

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

If you want to contribute, install your cloned fork:

git clone git@github.com:<your_fork>/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:

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

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

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

Now run migrations:

./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.

DJANGO_LOCI_GEOCODER

type:

str

default:

ArcGIS

Service used for geocoding and reverse geocoding.

Supported geolocation services:

  • ArcGIS

  • Nominatim

  • GoogleV3 (Google Maps v3)

DJANGO_LOCI_GEOCODE_FAILURE_DELAY

type:

int

default:

1

Amount of seconds between geocoding retry API calls when geocoding requests fail.

DJANGO_LOCI_GEOCODE_RETRIES

type:

int

default:

3

Amount of retry API calls when geocoding requests fail.

DJANGO_LOCI_GEOCODE_API_KEY

type:

str

default:

None

API key if required (eg: Google Maps).

System Checks

geocoding

Use to check if geocoding is working as expected or not.

Run this checks with:

./manage.py check --deploy --tag geocoding

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.

# 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:

INSTALLED_APPS = [
    # ...
    "django.contrib.gis",
    "django_loci",
    "django.contrib.admin",
    #      ↓
    "django.forms",  # <-- add this
    #      ↑
    "leaflet",
    "channels",
    # ...
]

Now add EXTENDED_APPS after INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
]

EXTENDED_APPS = ("django_loci",)

Add openwisp_utils.staticfiles.DependencyFinder to STATICFILES_FINDERS:

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

Add openwisp_utils.loaders.DependencyLoader to TEMPLATES:

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:

FORM_RENDERER = "django.forms.renderers.TemplatesSetting"

Then you can go ahead and create your admin.py file following the example below:

# 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:

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:

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:

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

Install your forked repo:

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

Install test requirements:

pip install -r requirements-test.txt

Create database:

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

Launch development server and SMTP debugging server:

./manage.py runserver

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

Run tests with (make sure you have the selenium dependencies installed locally first):

./runtests

Contributing

Please refer to the OpenWISP Contribution Guidelines.

Questions

See Github Discussions.

Changelog

See CHANGES.

License

See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_loci-1.2.tar.gz (47.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_loci-1.2-py2.py3-none-any.whl (53.6 kB view details)

Uploaded Python 2Python 3

File details

Details for the file django_loci-1.2.tar.gz.

File metadata

  • Download URL: django_loci-1.2.tar.gz
  • Upload date:
  • Size: 47.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_loci-1.2.tar.gz
Algorithm Hash digest
SHA256 fa7a6f45a77e4bc87bf2bdb3f28a150e441155adfe48f07dcebda5d0f1bc65de
MD5 17efa9c910fe38f89c270c2da289dcd7
BLAKE2b-256 23f7afbbd29c38bce3b146dc878cf6cd5dab57e77fa6fb6e07bc9d41f7275c30

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_loci-1.2.tar.gz:

Publisher: pypi.yml on openwisp/django-loci

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_loci-1.2-py2.py3-none-any.whl.

File metadata

  • Download URL: django_loci-1.2-py2.py3-none-any.whl
  • Upload date:
  • Size: 53.6 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_loci-1.2-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 3917934a13a8367faaac947749906148ae35fa2c44e1a4cbb1d2e421b4a4df00
MD5 49f75084689f935c9567837d1f4b87d2
BLAKE2b-256 049f12f65ecbbbc35a793ed9eeb606266a3a936cb0393909f3a4d1e6d9c103d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_loci-1.2-py2.py3-none-any.whl:

Publisher: pypi.yml on openwisp/django-loci

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page