Reusable django-app for outdoor and indoor mapping
Project description
Reusable django-app for storing GIS and indoor coordinates of objects.
Table of Contents:
Dependencies
- Python >= 3.7
- 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 - 0.4 | >=3.6 |
0.5 | >=3.7 |
Install stable version from pypi
Install from pypi:
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:
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.routing.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:
# pytests is used to test django-channels ./runtests.py && pytest
Contributing
- Announce your intentions in the OpenWISP Mailing List
- Fork this repo and install it
- Follow PEP8, Style Guide for Python Code
- Write code
- Write tests for your code
- Ensure all tests pass
- Ensure test coverage does not decrease
- Document your changes
- Send pull request
Changelog
See CHANGES.
License
See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_loci-1.0.1-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5819a27884217a461a433703c6758cd53ed3c4ee3d52bb956f5b92e8a1944e17 |
|
MD5 | aa6d2b28279e6d397fe6d164980f175a |
|
BLAKE2-256 | abd409ce1444cca3f3d0ee2032013c08e8f9f36faec01e70811dbfd7b2920674 |