Django制作地图¶
摘要¶
基于Django、Pillow和Leaflet制作的地图展示,可以理解为参考文章的”引进版“。由于大部分功能在参考文章中已经实现,故在此不多赘述,只介绍一些新的部分和改动,以及一些注意事项。
参考文章¶
Maps with Django⁽¹⁾: GeoDjango, SpatiaLite & Leaflet
Maps with Django⁽²⁾: GeoDjango, PostGIS & Leaflet
Maps with Django⁽³⁾: GeoDjango, Pillow & GPS
依赖¶
项目使用uv进行依赖管理,安装的依赖如下:
- django
- django-leaflet>=0.33.0
- pillow>=12.3.0
- django-unfold>=0.100.0
- GDAL
django安装最新版即可,其中GDAL需要单独安装,安装自己电脑系统和版本进行安装
GDAL¶
在安装django和gdal之后,根据django官方文档需要在settings.py中进行设置
VENV_SITE_PACKAGES = BASE_DIR / ".venv" / "Lib" / "site-packages"
proj_ltb = VENV_SITE_PACKAGES / "osgeo" / "data" / "proj"
os.environ["PROJ_LIB"] = proj_ltb.as_posix()
GDAL_LIBRARY_PATH = VENV_SITE_PACKAGES / "osgeo" / "gdal.dll"
GEOS_LIBRARY_PATH = VENV_SITE_PACKAGES / "osgeo" / "geos_c.dll"
一些改动¶
使用了django-leaflet替代了leaflet.js,所以一些地方需要进行修改
settings.py¶
LEAFLET_CONFIG = {
"DEFAULT_CENTER": (23.1291, 113.2644),
"DEFAULT_ZOOM": 12,
"MIN_ZOOM": 3,
"MAX_ZOOM": 18,
"TILES": [
(
"高德街道",
"https://wprd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7",
{
"subdomains": "1234",
"attribution": "© 高德地图",
},
),
# 高德卫星影像
(
"高德卫星",
"https://wprd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=6",
{
"subdomains": "1234",
"attribution": "© 高德地图",
},
),
],
"DEFAULT_TILES": "高德街道",
}
需要注意的是,参考文章中使用的OpenStreetMap瓦片源无法显示,这里替换成了国内的高德瓦片源。
map.html¶
{% load leaflet_tags %} {% load static %}
<!doctype html>
<html lang="en">
<head>
<title>Markers Map</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% leaflet_css %} {% leaflet_js %}
<style>
body {
margin: 0;
padding: 0;
}
html,
body,
#map {
height: 100%;
width: 100vw;
}
figure {
margin: 0;
padding: 0;
text-align: center;
}
img {
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
{{ markers|json_script:"markers" }}
<script src="{% static 'map.js' %}"></script>
{% leaflet_map "map" callback="window.map_init" %}
</body>
</html>
map.js¶
相对应的js也需进行修改
window.map_init = function (map, options) {
const data = JSON.parse(document.getElementById("markers").textContent);
const feature = L.geoJSON(data)
.bindPopup(
function (layer) {
const name = layer.feature.properties.name;
const img = layer.feature.properties.image;
if (!img) return name;
return (
"<figure>" +
'<img src="/media/' +
img.split("/").map(encodeURIComponent).join("/") +
'">' +
"<figcaption>" +
name +
"</figcaption>" +
"</figure>"
);
},
// 设置图片的最小宽度
{ minWidth: 200 },
)
.addTo(map);
const bounds = feature.getBounds();
if (bounds.isValid()) {
map.fitBounds(bounds);
}
};
新增¶
添加了unfold作为后台admin,具备现代化和美观
settings.py¶
将unfold添加到INSTALLED_APPS最上面
INSTALLED_APPS = [
"unfold",
"django_cotton",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.gis",
"leaflet",
"markers",
]
admin.py¶
from django.contrib import admin
from leaflet.admin import LeafletGeoAdmin
from unfold.admin import ModelAdmin
from markers.models import Marker
@admin.register(Marker)
class MarkerAdmin(LeafletGeoAdmin, ModelAdmin):
list_display = ("name", "image")
readonly_fields = ("location",)
search_fields = ("name",)
这里使用了LeafletGeoAdmin,而不是使用GISModelAdmin,也避免了与unfold的冲突。
apps.py¶
该文件需要在markers目录下新建,主要为了后台中文展示
from django.apps import AppConfig
class MarkersConfig(AppConfig):
name = "markers"
verbose_name = "地图标记"
models.py¶
添加了中文,方便后台使用
from django.contrib.gis.db import models
from django.core.validators import FileExtensionValidator
from django.db.models import Manager
from markers.utils import get_point
class Marker(models.Model):
objects: Manager
name = models.CharField(max_length=200, verbose_name="名称")
location = models.PointField(verbose_name="坐标")
image = models.ImageField(
verbose_name="图片",
null=True,
upload_to="images/markers/",
validators=[
FileExtensionValidator(allowed_extensions=["jpg", "jpeg"]),
],
)
class Meta:
verbose_name = "标记"
verbose_name_plural = "标记"
def __str__(self):
return str(self.name)
def save(self, *args, **kwargs):
if self.image:
point = get_point(self.image)
if point:
self.location = point
super().save(*args, **kwargs)
界面效果¶
其实该项目比较简单,使用django自带的admin足以,因为之前没用过这个后台,为了后续项目的开发,提前用这个项目对unfold做了测试,感觉还不错。
地图展示¶

结论¶
由于从事相关GIS行业,想着自己实现一次地图展示的功能,凭借对django的理解,根据国外文章进行的二次实现,实现了使用django进行地图的展示。其中用django-leaflet替换leaflet以及用unfold进行后台的美化,只是在原项目上一些微不足道的改动,也是对心中想法的一次尝试。