# π©» πΊοΈ django-large-image
<p align="center">
<a href="https://www.kitware.com/" target="_blank">
<img src="https://img.shields.io/badge/Made%20by-Kitware-blue" alt="Made by Kitware">
</a>
<a href="https://pypi.org/project/django-large-image/" target="_blank">
<img src="https://img.shields.io/pypi/v/django-large-image.svg?logo=python&logoColor=white" alt="PyPI">
</a>
<a href="https://anaconda.org/conda-forge/django-large-image" target="_blank">
<img src="https://img.shields.io/conda/vn/conda-forge/django-large-image.svg?logo=conda-forge&logoColor=white" alt="conda-forge">
</a>
<a href="https://codecov.io/gh/girder/django-large-image" target="_blank">
<img src="https://codecov.io/gh/girder/django-large-image/branch/main/graph/badge.svg?token=VBK1F6JWNY" alt="codecov">
</a>
<a href="https://github.com/girder/django-large-image/actions/workflows/ci.yml" target="_blank">
<img src="https://github.com/girder/django-large-image/actions/workflows/ci.yml/badge.svg" alt="Tests">
</a>
</p>
`django-large-image` is an abstraction of [`large-image`](https://github.com/girder/large_image)
for use with `django-rest-framework` providing viewset mixins for endpoints to
work with large images (Cloud Optimized GeoTiffs or medical image formats) in
Django. The dynamic tile server provided here prevents the need for
preprocessing large images into tile sets for viewing interactively on
slippy-maps. Under the hood, large-image applies operations (rescaling,
reprojection, image encoding) to create image tiles on-the-fly.
| Lightning Talk for 2022 Cloud-Native Geospatial Outreach Event |
|-|
| [![outreach event video](https://raw.githubusercontent.com/girder/django-large-image/main/doc/outreach_video.png)](https://youtu.be/v3e2ODCK9Co?t=31247) |
| [View slides here](https://docs.google.com/presentation/d/1T_bmtxx1qR8GgzXdFer3LwDi_dxp6X4RqndbsSVhWTg/edit?usp=sharing) |
## Table of Contents
- [Overview](#-overview)
- [Support](#-support)
- [Features](#-features)
- [Installation](#%EF%B8%8F-installation)
- [Usage](#-usage)
- [Example Code](#-example-code)
- [Customization](#%EF%B8%8F-customization)
- [Non-Detail ViewSets](#-non-detail-viewsets)
- [Styling](#-styling)
- [Converting Images to Pyramidal Tiffs (COGs)](#%EF%B8%8F-converting-images-to-pyramidal-tiffs-cogs)
- [Using with django-raster](#using-with-django-raster)
- [Demo App](#demo-app)
***
## βΉοΈ Overview
This package brings Kitware's [large-image](https://github.com/girder/large_image)
to Django by providing a set of abstract, mixin API viewset classes that will
handle tile serving, fetching metadata from images, and extracting regions of
interest.
`django-large-image` is an installable Django app with
a few classes that can be mixed into a Django project (or application)'s
drf-based viewsets to provide tile serving endpoints out of the box. Notably,
`django-large-image` is designed to work specifically with `FileField`
interfaces with development being tailored to Kitware's
[`S3FileField`](https://github.com/girder/django-s3-file-field). GeoDjango's [`GDALRaster`](https://docs.djangoproject.com/en/4.0/ref/contrib/gis/gdal/#django.contrib.gis.gdal.GDALRaster)
can also be used by returning `GDALRaster.name` in the `get_path()` override.
This package ships with pre-made HTML templates for rendering geospatial image
tiles with CesiumJS and non-geospatial image tiles with [GeoJS](https://github.com/OpenGeoscience/geojs).
<p align="center">
<img src="https://raw.githubusercontent.com/girder/django-large-image/main/doc/admin.png"/>
<p align="center">Dynamic tile server in Django built on top of large-image (and GDAL)</p>
</p>
### π€ Support
[![Kitware](https://img.shields.io/badge/Made%20by-Kitware-blue)](https://www.kitware.com/)
`django-large-image` and the supporting [`large-image`](https://github.com/girder/large_image)
library are developed and maintained by the Data & Analytics group at
[Kitware, Inc.](https://www.kitware.com/)
We work with large image data in both the geospatial and medical capacities.
If you have questions about these technologies, or you would like to discuss
your own geospatial and medical image problems and learn how we can help,
please reach out at kitware@kitware.com. We look forward to the conversation!
### π Features
Rich set of RESTful endpoints to extract information from large image formats:
- Image metadata (`/info/metadata`, `/info/metadata_internal`)
- Tile serving (`/tiles/{z}/{x}/{y}.png?projection=EPSG:3857`)
- Region extraction (`/data/region.tif?left=v&right=v&top=v&bottom=v`)
- Image thumbnails (`/data/thumbnail.png`)
- Individual pixels (`/data/pixel?left=v&top=v`)
- Band histograms (`/data/histogram`)
Support for any storage backend:
- Supports Django's `FileField`
- Supports [`S3FileField`](https://github.com/girder/django-s3-file-field)
- Customizable method for handling data access (`get_path` override)
- Supports GDAL's [Virtual File System](https://gdal.org/user/virtual_file_systems.html) for `s3://`, `ftp://`, etc. URLs
Miscellaneous:
- Admin interface widget for viewing image tiles.
- Caching
- image tiles and thumbnails are cached to prevent recreating these data on multiple requests
- utilizes the [Django cache framework](https://docs.djangoproject.com/en/4.0/topics/cache/). Specify a named cache to use with the `LARGE_IMAGE_CACHE_NAME` setting.
- Easily extensible SSR templates for tile viewing with CesiumJS and GeoJS
- OpenAPI specification
| OpenAPI Documentation | Tiles Endpoint |
|---|---|
|![swagger-spec](https://raw.githubusercontent.com/girder/django-large-image/main/doc/swagger.png) | ![tiles-spec](https://raw.githubusercontent.com/girder/django-large-image/main/doc/tiles_endpoint.png)|
## β¬οΈ Installation
Out of the box, `django-large-image` only depends on the core `large-image`
module, but you will need a `large-image-source-*` module in order for this
to work. Most of our users probably want to work with geospatial images so we
will focus on the `large-image-source-gdal` and ``large-image-source-rasterio`
cases, but it is worth noting that `large-image` has source modules for a wide
variety of image formats (e.g., medical image formats for microscopy).
See [`large-image`](https://github.com/girder/large_image#installation)'s
installation instructions for more details.
### π‘ pip
### Rasterio
```bash
pip install \
django-large-image \
'large-image[rasterio,pil]>=1.22'
```
### GDAL
**Tip:* installing GDAL is notoriously difficult, so at Kitware we provide
pre-built Python wheels with the GDAL binary bundled for easily installation in
production **linux** environments. To install our GDAL wheel, use:
`pip install --find-links https://girder.github.io/large_image_wheels GDAL`*
```bash
pip install \
--find-links https://girder.github.io/large_image_wheels \
django-large-image \
'large-image[gdal,pil]>=1.16.2'
```
### π Conda
Or install with `conda`:
```bash
conda install -c conda-forge django-large-image large-image-source-rasterio
```
```bash
conda install -c conda-forge django-large-image large-image-source-gdal
```
## π Usage
Simply install the app and mixin one of the mixing classes to your
existing `django-rest-framework` viewset.
```py
# settings.py
INSTALLED_APPS = [
...,
'django_large_image',
]
```
The following are the provided mixin classes and their use case:
- `LargeImageMixin`: for use with a standard, non-detail `ViewSet`. Users must implement `get_path()`
- `LargeImageDetailMixin`: for use with a detail viewset like `GenericViewSet`. Users must implement `get_path()`
- `LargeImageFileDetailMixin`: (most commonly used) for use with a detail viewset like `GenericViewSet` where the associated model has a `FileField` storing the image data.
- `LargeImageVSIFileDetailMixin`: (geospatial) for use with a detail viewset like `GenericViewSet` where the associated model has a `FileField` storing the image data that is intended to be read with GDAL/rasterio. This will access the data over GDAL's Virtual File System interface (a VSI path).
Most users will want to use `LargeImageFileDetailMixin` and so the following
example demonstrate how to use it:
Specify the `FILE_FIELD_NAME` as the string name of the `FileField` in which
your image data are saved on the associated model.
```py
# viewsets.py
from rest_framework import viewsets
from django_large_image.rest import LargeImageFileDetailMixin
class MyModelViewSet(viewsets.GenericViewSet, LargeImageFileDetailMixin):
... # configuration for your model's viewset
FILE_FIELD_NAME = 'field_name'
```
```py
# urls.py
from django.urls import include, path
from rest_framework.routers import SimpleRouter
from myapp.viewsets import MyModelViewSet
router = SimpleRouter(trailing_slash=False)
router.register(r'api/my-model', MyModelViewSet)
urlpatterns = [
# Additional, standalone URLs from django-large-image
path('', include('django_large_image.urls')),
] + router.urls
```
And that's it!
### π Example Code
To use the mixin classes provided here, add `django_large_image` to the
`INSTALLED_APPS` of your Django project, then create a model, serializer,
and viewset in your Django project like so:
```py
# models.py
from django.db import models
from rest_framework import serializers
class ImageFile(models.Model):
name = models.TextField()
file = models.FileField()
class ImageFileSerializer(serializers.ModelSerializer):
class Meta:
model = ImageFile
fields = '__all__'
```
```py
# admin.py
from django.contrib import admin
from example.core.models import ImageFile
@admin.register(ImageFile)
class ImageFileAdmin(admin.ModelAdmin):
list_display = ('pk', 'name')
```
Then create the viewset, mixing in the `django-large-image` viewset class:
```py
# viewsets.py
from example.core import models
from rest_framework import mixins, viewsets
from django_large_image.rest import LargeImageFileDetailMixin
class ImageFileDetailViewSet(
mixins.ListModelMixin,
viewsets.GenericViewSet,
LargeImageFileDetailMixin,
):
queryset = models.ImageFile.objects.all()
serializer_class = models.ImageFileSerializer
# for `django-large-image`: the name of the image FileField on your model
FILE_FIELD_NAME = 'file'
```
Then register the URLs:
```py
# urls.py
from django.urls import include, path
from example.core.viewsets import ImageFileDetailViewSet
from rest_framework.routers import SimpleRouter
router = SimpleRouter(trailing_slash=False)
router.register(r'api/image-file', ImageFileDetailViewSet)
urlpatterns = [
# Additional, standalone URLs from django-large-image
path('', include('django_large_image.urls')),
] + router.urls
```
(Optional) You can also use an admin widget for your model:
```html
<!-- templates/admin/myapp/imagefile/change_form.html -->
{% extends "admin/change_form.html" %}
{% block after_field_sets %}
<script>
var baseEndpoint = 'api/image-file';
</script>
{% include 'admin/django_large_image/_include/geojs.html' %}
{% endblock %}
```
Please note the example Django project in the `project/` directory of this
repository that shows how to use `django-large-image` in a [`girder-4`](https://github.com/girder/cookiecutter-girder-4) project.
### π οΈ Customization
The mixin classes are modularly designed and able to be subclassed
for your project's needs. While the provided `LargeImageFileDetailMixin` handles
`FileField`-interfaces, you can easily extend its base class,
`LargeImageDetailMixin`, to handle any mechanism of data storage in your
detail-oriented viewset.
In the following example, we demonstrate how to use GDAL compatible VSI paths
from a model that stores `s3://` or `https://` URLs.
```py
# models.py
from django.db import models
from rest_framework import serializers
class URLImageFile(models.Model):
name = models.TextField()
url = models.TextField()
class URLImageFileSerializer(serializers.ModelSerializer):
class Meta:
model = URLImageFile
fields = '__all__'
```
```py
# admin.py
from django.contrib import admin
from example.core.models import URLImageFile
@admin.register(URLImageFile)
class URLImageFileAdmin(admin.ModelAdmin):
list_display = ('pk', 'name')
```
```py
# viewsets.py
from example.core import models
from rest_framework import mixins, viewsets
from django_large_image.rest import LargeImageDetailMixin
from django_large_image.utilities import make_vsi
class URLLargeImageMixin(LargeImageDetailMixin):
def get_path(self, request, pk=None):
object = self.get_object()
return make_vsi(object.url)
class URLImageFileDetailViewSet(
mixins.ListModelMixin,
viewsets.GenericViewSet,
URLLargeImageMixin,
):
queryset = models.URLImageFile.objects.all()
serializer_class = models.URLImageFileSerializer
```
Here is a good test image: https://oin-hotosm.s3.amazonaws.com/59c66c5223c8440011d7b1e4/0/7ad397c0-bba2-4f98-a08a-931ec3a6e943.tif
#### π₯Έ Non-Detail ViewSets
The `LargeImageMixin` provides a mixin interface for non-detail viewsets (no
associated model or primary key required). This can be particularly useful if
your viewset has custom logic to retrieve the desired data.
For example, you may want a viewset that gets the data path as a URL embedded
in the request's query parameters. To do this, you can make a standard ViewSet
with the `LargeImageMixin` like so:
```py
# viewsets.py
from rest_framework import viewsets
from rest_framework.exceptions import ValidationError
from django_large_image.rest import LargeImageMixin
from django_large_image.utilities import make_vsi
class URLLargeImageViewSet(viewsets.ViewSet, LargeImageMixin):
def get_path(self, request, pk=None):
try:
url = request.query_params.get('url')
except KeyError:
raise ValidationError('url must be defined as a query parameter.')
return make_vsi(url)
```
## πͺ Styling
`django-large-image`'s dynamic tile serving supports band styling and making
composite images from multiple frames and/or bands of your images. This means
that you can easily create a false color image from multispectral imagery.
`django-large-image` has two styling modes:
1. A simple interface to colormap a single channel using multiple query parameters. These are the documented OpenAPI query parameters.
View a single band with a Matplotlib colormap:
```js
var thumbnailUrl = `http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?band=3&palette=viridis&min=50&max=250`;
```
2. A complex specification for styling across frames and bands to create composite images using a [JSON specification defined by `large-image`](https://girder.github.io/large_image/tilesource_options.html#style).
Create a false color image from multiple bands in the source image:
```js
// See https://girder.github.io/large_image/tilesource_options.html#style
var style = {
bands: [
{band: 5, palette: ['#000', '#f00']}, // red
{band: 3, palette: ['#000', '#0f0']}, // green
{band: 2, palette: ['#000', '#00f']} // blue
]
};
var styleEncoded = encodeURIComponent(JSON.stringify(style))
var thumbnailUrl = `http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?style=${styleEncoded}`;
```
## βοΈ Converting Images to Pyramidal Tiffs (COGs)
Install [`large_image_converter`](https://pypi.org/project/large-image-converter/) and run the following:
```py
import large_image_converter
large_image_converter.convert(input_path, output_path)
```
It's that easy! The default parameters for that function will convert
geospatial rasters to Cloud Optimized GeoTiffs (COGs) and non-geospatial images
to a pyramidal tiff format.
It's quite common to have a celery task that converts an image from a
model in your application. Here is a starting point:
```py
import os
from example.core import models
from celery import shared_task
import large_image_converter # requires large-image-source-gdal
@shared_task
def task_convert_cog(my_model_pk):
image_file = models.ImageFile.objects.get(pk=my_model_pk)
input_path = image_file.file.name # TODO: get full path to file on disk
with tempfile.TemporaryDirectory() as tmpdir:
output_path = os.path.join(tmpdir, 'converted.tiff')
large_image_converter.convert(input_path, output_path)
# Do something with converted tiff file at `output_path`
...
```
If using the `rasterio`-based source module, we recommend using
[`rio-cogeo`](https://github.com/cogeotiff/rio-cogeo)
over `large_image_converter`.
## Using with django-raster
[`django-raster`](https://github.com/geodesign/django-raster) is a popular
choice for storing geospatial raster data in Django. `django-large-image` works
well with `django-raster` to provide additional endpoints for dynamic tile
serving and more.
Please take a look at the demo project here: https://github.com/ResonantGeoData/django-raster-demo
and raise any questions about usage with `django-raster` there.
## Demo App
There is a vanilla Django project in the `demo/` directory and this app
is published as a standalone Docker image that anyone can try out:
```bash
docker run -it -p 8000:8000 -v dli_demo_data:/opt/django-project/data ghcr.io/girder/django-large-image-demo:latest
```
Raw data
{
"_id": null,
"home_page": "https://github.com/girder/django-large-image",
"name": "django-large-image",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "",
"author": "Kitware, Inc.",
"author_email": "kitware@kitware.com",
"download_url": "https://files.pythonhosted.org/packages/65/d4/2ef11c816268cff24f70658bbcad600a361e3f937b65a24404895dcf5ef1/django-large-image-0.10.0.tar.gz",
"platform": null,
"description": "# \ud83e\ude7b \ud83d\uddfa\ufe0f django-large-image\n\n<p align=\"center\">\n <a href=\"https://www.kitware.com/\" target=\"_blank\">\n <img src=\"https://img.shields.io/badge/Made%20by-Kitware-blue\" alt=\"Made by Kitware\">\n </a>\n <a href=\"https://pypi.org/project/django-large-image/\" target=\"_blank\">\n <img src=\"https://img.shields.io/pypi/v/django-large-image.svg?logo=python&logoColor=white\" alt=\"PyPI\">\n </a>\n <a href=\"https://anaconda.org/conda-forge/django-large-image\" target=\"_blank\">\n <img src=\"https://img.shields.io/conda/vn/conda-forge/django-large-image.svg?logo=conda-forge&logoColor=white\" alt=\"conda-forge\">\n </a>\n <a href=\"https://codecov.io/gh/girder/django-large-image\" target=\"_blank\">\n <img src=\"https://codecov.io/gh/girder/django-large-image/branch/main/graph/badge.svg?token=VBK1F6JWNY\" alt=\"codecov\">\n </a>\n <a href=\"https://github.com/girder/django-large-image/actions/workflows/ci.yml\" target=\"_blank\">\n <img src=\"https://github.com/girder/django-large-image/actions/workflows/ci.yml/badge.svg\" alt=\"Tests\">\n </a>\n</p>\n\n\n`django-large-image` is an abstraction of [`large-image`](https://github.com/girder/large_image)\nfor use with `django-rest-framework` providing viewset mixins for endpoints to\nwork with large images (Cloud Optimized GeoTiffs or medical image formats) in\nDjango. The dynamic tile server provided here prevents the need for\npreprocessing large images into tile sets for viewing interactively on\nslippy-maps. Under the hood, large-image applies operations (rescaling,\nreprojection, image encoding) to create image tiles on-the-fly.\n\n| Lightning Talk for 2022 Cloud-Native Geospatial Outreach Event |\n|-|\n| [![outreach event video](https://raw.githubusercontent.com/girder/django-large-image/main/doc/outreach_video.png)](https://youtu.be/v3e2ODCK9Co?t=31247) |\n| [View slides here](https://docs.google.com/presentation/d/1T_bmtxx1qR8GgzXdFer3LwDi_dxp6X4RqndbsSVhWTg/edit?usp=sharing) |\n\n## Table of Contents\n\n- [Overview](#-overview)\n - [Support](#-support)\n - [Features](#-features)\n- [Installation](#%EF%B8%8F-installation)\n- [Usage](#-usage)\n - [Example Code](#-example-code)\n - [Customization](#%EF%B8%8F-customization)\n - [Non-Detail ViewSets](#-non-detail-viewsets)\n - [Styling](#-styling)\n - [Converting Images to Pyramidal Tiffs (COGs)](#%EF%B8%8F-converting-images-to-pyramidal-tiffs-cogs)\n - [Using with django-raster](#using-with-django-raster)\n- [Demo App](#demo-app)\n\n***\n\n## \u2139\ufe0f Overview\n\nThis package brings Kitware's [large-image](https://github.com/girder/large_image)\nto Django by providing a set of abstract, mixin API viewset classes that will\nhandle tile serving, fetching metadata from images, and extracting regions of\ninterest.\n\n`django-large-image` is an installable Django app with\na few classes that can be mixed into a Django project (or application)'s\ndrf-based viewsets to provide tile serving endpoints out of the box. Notably,\n`django-large-image` is designed to work specifically with `FileField`\ninterfaces with development being tailored to Kitware's\n[`S3FileField`](https://github.com/girder/django-s3-file-field). GeoDjango's [`GDALRaster`](https://docs.djangoproject.com/en/4.0/ref/contrib/gis/gdal/#django.contrib.gis.gdal.GDALRaster)\ncan also be used by returning `GDALRaster.name` in the `get_path()` override.\n\nThis package ships with pre-made HTML templates for rendering geospatial image\ntiles with CesiumJS and non-geospatial image tiles with [GeoJS](https://github.com/OpenGeoscience/geojs).\n\n<p align=\"center\">\n <img src=\"https://raw.githubusercontent.com/girder/django-large-image/main/doc/admin.png\"/>\n <p align=\"center\">Dynamic tile server in Django built on top of large-image (and GDAL)</p>\n</p>\n\n### \ud83e\udd1d Support\n[![Kitware](https://img.shields.io/badge/Made%20by-Kitware-blue)](https://www.kitware.com/)\n\n`django-large-image` and the supporting [`large-image`](https://github.com/girder/large_image)\nlibrary are developed and maintained by the Data & Analytics group at\n[Kitware, Inc.](https://www.kitware.com/)\nWe work with large image data in both the geospatial and medical capacities.\nIf you have questions about these technologies, or you would like to discuss\nyour own geospatial and medical image problems and learn how we can help,\nplease reach out at kitware@kitware.com. We look forward to the conversation!\n\n### \ud83c\udf1f Features\n\nRich set of RESTful endpoints to extract information from large image formats:\n- Image metadata (`/info/metadata`, `/info/metadata_internal`)\n- Tile serving (`/tiles/{z}/{x}/{y}.png?projection=EPSG:3857`)\n- Region extraction (`/data/region.tif?left=v&right=v&top=v&bottom=v`)\n- Image thumbnails (`/data/thumbnail.png`)\n- Individual pixels (`/data/pixel?left=v&top=v`)\n- Band histograms (`/data/histogram`)\n\nSupport for any storage backend:\n- Supports Django's `FileField`\n- Supports [`S3FileField`](https://github.com/girder/django-s3-file-field)\n- Customizable method for handling data access (`get_path` override)\n- Supports GDAL's [Virtual File System](https://gdal.org/user/virtual_file_systems.html) for `s3://`, `ftp://`, etc. URLs\n\nMiscellaneous:\n- Admin interface widget for viewing image tiles.\n- Caching\n - image tiles and thumbnails are cached to prevent recreating these data on multiple requests\n - utilizes the [Django cache framework](https://docs.djangoproject.com/en/4.0/topics/cache/). Specify a named cache to use with the `LARGE_IMAGE_CACHE_NAME` setting.\n- Easily extensible SSR templates for tile viewing with CesiumJS and GeoJS\n- OpenAPI specification\n\n| OpenAPI Documentation | Tiles Endpoint |\n|---|---|\n|![swagger-spec](https://raw.githubusercontent.com/girder/django-large-image/main/doc/swagger.png) | ![tiles-spec](https://raw.githubusercontent.com/girder/django-large-image/main/doc/tiles_endpoint.png)|\n\n## \u2b07\ufe0f Installation\n\nOut of the box, `django-large-image` only depends on the core `large-image`\nmodule, but you will need a `large-image-source-*` module in order for this\nto work. Most of our users probably want to work with geospatial images so we\nwill focus on the `large-image-source-gdal` and ``large-image-source-rasterio`\ncases, but it is worth noting that `large-image` has source modules for a wide\nvariety of image formats (e.g., medical image formats for microscopy).\n\nSee [`large-image`](https://github.com/girder/large_image#installation)'s\ninstallation instructions for more details.\n\n\n### \ud83c\udfa1 pip\n\n### Rasterio\n\n```bash\npip install \\\n django-large-image \\\n 'large-image[rasterio,pil]>=1.22'\n```\n\n### GDAL\n\n**Tip:* installing GDAL is notoriously difficult, so at Kitware we provide\npre-built Python wheels with the GDAL binary bundled for easily installation in\nproduction **linux** environments. To install our GDAL wheel, use:\n`pip install --find-links https://girder.github.io/large_image_wheels GDAL`*\n\n\n```bash\npip install \\\n --find-links https://girder.github.io/large_image_wheels \\\n django-large-image \\\n 'large-image[gdal,pil]>=1.16.2'\n```\n\n### \ud83d\udc0d Conda\n\nOr install with `conda`:\n\n```bash\nconda install -c conda-forge django-large-image large-image-source-rasterio\n```\n\n```bash\nconda install -c conda-forge django-large-image large-image-source-gdal\n```\n\n\n## \ud83d\ude80 Usage\n\nSimply install the app and mixin one of the mixing classes to your\nexisting `django-rest-framework` viewset.\n\n```py\n# settings.py\nINSTALLED_APPS = [\n ...,\n 'django_large_image',\n]\n```\n\nThe following are the provided mixin classes and their use case:\n\n- `LargeImageMixin`: for use with a standard, non-detail `ViewSet`. Users must implement `get_path()`\n- `LargeImageDetailMixin`: for use with a detail viewset like `GenericViewSet`. Users must implement `get_path()`\n- `LargeImageFileDetailMixin`: (most commonly used) for use with a detail viewset like `GenericViewSet` where the associated model has a `FileField` storing the image data.\n- `LargeImageVSIFileDetailMixin`: (geospatial) for use with a detail viewset like `GenericViewSet` where the associated model has a `FileField` storing the image data that is intended to be read with GDAL/rasterio. This will access the data over GDAL's Virtual File System interface (a VSI path).\n\nMost users will want to use `LargeImageFileDetailMixin` and so the following\nexample demonstrate how to use it:\n\nSpecify the `FILE_FIELD_NAME` as the string name of the `FileField` in which\nyour image data are saved on the associated model.\n\n```py\n# viewsets.py\nfrom rest_framework import viewsets\nfrom django_large_image.rest import LargeImageFileDetailMixin\n\nclass MyModelViewSet(viewsets.GenericViewSet, LargeImageFileDetailMixin):\n ... # configuration for your model's viewset\n FILE_FIELD_NAME = 'field_name'\n```\n\n```py\n# urls.py\nfrom django.urls import include, path\nfrom rest_framework.routers import SimpleRouter\n\nfrom myapp.viewsets import MyModelViewSet\n\nrouter = SimpleRouter(trailing_slash=False)\nrouter.register(r'api/my-model', MyModelViewSet)\n\nurlpatterns = [\n # Additional, standalone URLs from django-large-image\n path('', include('django_large_image.urls')),\n] + router.urls\n```\n\nAnd that's it!\n\n### \ud83d\udcdd Example Code\n\nTo use the mixin classes provided here, add `django_large_image` to the\n`INSTALLED_APPS` of your Django project, then create a model, serializer,\nand viewset in your Django project like so:\n\n```py\n# models.py\nfrom django.db import models\nfrom rest_framework import serializers\n\n\nclass ImageFile(models.Model):\n name = models.TextField()\n file = models.FileField()\n\n\nclass ImageFileSerializer(serializers.ModelSerializer):\n class Meta:\n model = ImageFile\n fields = '__all__'\n```\n\n```py\n# admin.py\nfrom django.contrib import admin\nfrom example.core.models import ImageFile\n\n\n@admin.register(ImageFile)\nclass ImageFileAdmin(admin.ModelAdmin):\n list_display = ('pk', 'name')\n```\n\nThen create the viewset, mixing in the `django-large-image` viewset class:\n```py\n# viewsets.py\nfrom example.core import models\nfrom rest_framework import mixins, viewsets\n\nfrom django_large_image.rest import LargeImageFileDetailMixin\n\n\nclass ImageFileDetailViewSet(\n mixins.ListModelMixin,\n viewsets.GenericViewSet,\n LargeImageFileDetailMixin,\n):\n queryset = models.ImageFile.objects.all()\n serializer_class = models.ImageFileSerializer\n\n # for `django-large-image`: the name of the image FileField on your model\n FILE_FIELD_NAME = 'file'\n```\n\nThen register the URLs:\n\n```py\n# urls.py\nfrom django.urls import include, path\nfrom example.core.viewsets import ImageFileDetailViewSet\nfrom rest_framework.routers import SimpleRouter\n\nrouter = SimpleRouter(trailing_slash=False)\nrouter.register(r'api/image-file', ImageFileDetailViewSet)\n\nurlpatterns = [\n # Additional, standalone URLs from django-large-image\n path('', include('django_large_image.urls')),\n] + router.urls\n```\n\n(Optional) You can also use an admin widget for your model:\n\n```html\n<!-- templates/admin/myapp/imagefile/change_form.html -->\n{% extends \"admin/change_form.html\" %}\n\n{% block after_field_sets %}\n\n<script>\n var baseEndpoint = 'api/image-file';\n</script>\n\n{% include 'admin/django_large_image/_include/geojs.html' %}\n\n{% endblock %}\n```\n\nPlease note the example Django project in the `project/` directory of this\nrepository that shows how to use `django-large-image` in a [`girder-4`](https://github.com/girder/cookiecutter-girder-4) project.\n\n\n### \ud83d\udee0\ufe0f Customization\n\nThe mixin classes are modularly designed and able to be subclassed\nfor your project's needs. While the provided `LargeImageFileDetailMixin` handles\n`FileField`-interfaces, you can easily extend its base class,\n`LargeImageDetailMixin`, to handle any mechanism of data storage in your\ndetail-oriented viewset.\n\nIn the following example, we demonstrate how to use GDAL compatible VSI paths\nfrom a model that stores `s3://` or `https://` URLs.\n\n```py\n# models.py\nfrom django.db import models\nfrom rest_framework import serializers\n\n\nclass URLImageFile(models.Model):\n name = models.TextField()\n url = models.TextField()\n\n\nclass URLImageFileSerializer(serializers.ModelSerializer):\n class Meta:\n model = URLImageFile\n fields = '__all__'\n```\n\n```py\n# admin.py\nfrom django.contrib import admin\nfrom example.core.models import URLImageFile\n\n\n@admin.register(URLImageFile)\nclass URLImageFileAdmin(admin.ModelAdmin):\n list_display = ('pk', 'name')\n```\n\n\n```py\n# viewsets.py\nfrom example.core import models\nfrom rest_framework import mixins, viewsets\n\nfrom django_large_image.rest import LargeImageDetailMixin\nfrom django_large_image.utilities import make_vsi\n\n\nclass URLLargeImageMixin(LargeImageDetailMixin):\n def get_path(self, request, pk=None):\n object = self.get_object()\n return make_vsi(object.url)\n\n\nclass URLImageFileDetailViewSet(\n mixins.ListModelMixin,\n viewsets.GenericViewSet,\n URLLargeImageMixin,\n):\n queryset = models.URLImageFile.objects.all()\n serializer_class = models.URLImageFileSerializer\n```\n\nHere is a good test image: https://oin-hotosm.s3.amazonaws.com/59c66c5223c8440011d7b1e4/0/7ad397c0-bba2-4f98-a08a-931ec3a6e943.tif\n\n\n#### \ud83e\udd78 Non-Detail ViewSets\n\nThe `LargeImageMixin` provides a mixin interface for non-detail viewsets (no\nassociated model or primary key required). This can be particularly useful if\nyour viewset has custom logic to retrieve the desired data.\n\nFor example, you may want a viewset that gets the data path as a URL embedded\nin the request's query parameters. To do this, you can make a standard ViewSet\nwith the `LargeImageMixin` like so:\n\n```py\n# viewsets.py\nfrom rest_framework import viewsets\nfrom rest_framework.exceptions import ValidationError\n\nfrom django_large_image.rest import LargeImageMixin\nfrom django_large_image.utilities import make_vsi\n\n\nclass URLLargeImageViewSet(viewsets.ViewSet, LargeImageMixin):\n def get_path(self, request, pk=None):\n try:\n url = request.query_params.get('url')\n except KeyError:\n raise ValidationError('url must be defined as a query parameter.')\n return make_vsi(url)\n\n```\n\n## \ud83e\ude84 Styling\n\n`django-large-image`'s dynamic tile serving supports band styling and making\ncomposite images from multiple frames and/or bands of your images. This means\nthat you can easily create a false color image from multispectral imagery.\n\n`django-large-image` has two styling modes:\n\n1. A simple interface to colormap a single channel using multiple query parameters. These are the documented OpenAPI query parameters.\n\nView a single band with a Matplotlib colormap:\n\n```js\nvar thumbnailUrl = `http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?band=3&palette=viridis&min=50&max=250`;\n```\n\n2. A complex specification for styling across frames and bands to create composite images using a [JSON specification defined by `large-image`](https://girder.github.io/large_image/tilesource_options.html#style).\n\nCreate a false color image from multiple bands in the source image:\n\n```js\n// See https://girder.github.io/large_image/tilesource_options.html#style\nvar style = {\n bands: [\n {band: 5, palette: ['#000', '#f00']}, // red\n {band: 3, palette: ['#000', '#0f0']}, // green\n {band: 2, palette: ['#000', '#00f']} // blue\n ]\n};\nvar styleEncoded = encodeURIComponent(JSON.stringify(style))\nvar thumbnailUrl = `http://localhost:8000/api/image-file/${imageId}/data/thumbnail.png?style=${styleEncoded}`;\n```\n\n\n## \u2601\ufe0f Converting Images to Pyramidal Tiffs (COGs)\n\nInstall [`large_image_converter`](https://pypi.org/project/large-image-converter/) and run the following:\n\n```py\nimport large_image_converter\nlarge_image_converter.convert(input_path, output_path)\n```\n\nIt's that easy! The default parameters for that function will convert\ngeospatial rasters to Cloud Optimized GeoTiffs (COGs) and non-geospatial images\nto a pyramidal tiff format.\n\nIt's quite common to have a celery task that converts an image from a\nmodel in your application. Here is a starting point:\n\n```py\nimport os\nfrom example.core import models\nfrom celery import shared_task\nimport large_image_converter # requires large-image-source-gdal\n\n\n@shared_task\ndef task_convert_cog(my_model_pk):\n image_file = models.ImageFile.objects.get(pk=my_model_pk)\n input_path = image_file.file.name # TODO: get full path to file on disk\n\n with tempfile.TemporaryDirectory() as tmpdir:\n output_path = os.path.join(tmpdir, 'converted.tiff')\n large_image_converter.convert(input_path, output_path)\n\n # Do something with converted tiff file at `output_path`\n ...\n```\n\nIf using the `rasterio`-based source module, we recommend using\n[`rio-cogeo`](https://github.com/cogeotiff/rio-cogeo)\nover `large_image_converter`.\n\n## Using with django-raster\n\n[`django-raster`](https://github.com/geodesign/django-raster) is a popular\nchoice for storing geospatial raster data in Django. `django-large-image` works\nwell with `django-raster` to provide additional endpoints for dynamic tile\nserving and more.\n\nPlease take a look at the demo project here: https://github.com/ResonantGeoData/django-raster-demo\nand raise any questions about usage with `django-raster` there.\n\n\n\n## Demo App\n\nThere is a vanilla Django project in the `demo/` directory and this app\nis published as a standalone Docker image that anyone can try out:\n\n```bash\ndocker run -it -p 8000:8000 -v dli_demo_data:/opt/django-project/data ghcr.io/girder/django-large-image-demo:latest\n```\n",
"bugtrack_url": null,
"license": "Apache 2.0",
"summary": "Dynamic tile server in Django built on top of large-image (and GDAL)",
"version": "0.10.0",
"project_urls": {
"Homepage": "https://github.com/girder/django-large-image"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d0c7a93f5ebfdd726dbd195210c19f1fba764352c8852f3f075abda96fb1a8c4",
"md5": "d3ed286a9b15f923cc2e2c4e23410606",
"sha256": "482a26893faec3856d039fc9d6926fd6baaa41477d1f7bdb23f3558ba2686d0c"
},
"downloads": -1,
"filename": "django_large_image-0.10.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d3ed286a9b15f923cc2e2c4e23410606",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 956532,
"upload_time": "2023-05-29T22:30:20",
"upload_time_iso_8601": "2023-05-29T22:30:20.229874Z",
"url": "https://files.pythonhosted.org/packages/d0/c7/a93f5ebfdd726dbd195210c19f1fba764352c8852f3f075abda96fb1a8c4/django_large_image-0.10.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "65d42ef11c816268cff24f70658bbcad600a361e3f937b65a24404895dcf5ef1",
"md5": "d09c80be5ee308c7be26b92df86e7631",
"sha256": "97b564d8eee6adf5b8a65cf71cdd5cea5362fa794ff80583e045e028d03817bd"
},
"downloads": -1,
"filename": "django-large-image-0.10.0.tar.gz",
"has_sig": false,
"md5_digest": "d09c80be5ee308c7be26b92df86e7631",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 949871,
"upload_time": "2023-05-29T22:30:22",
"upload_time_iso_8601": "2023-05-29T22:30:22.895247Z",
"url": "https://files.pythonhosted.org/packages/65/d4/2ef11c816268cff24f70658bbcad600a361e3f937b65a24404895dcf5ef1/django-large-image-0.10.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-05-29 22:30:22",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "girder",
"github_project": "django-large-image",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "django-large-image"
}