Skip to content

Commit 7971b5c

Browse files
authored
Merge pull request #250 from OpenGeoscience/base-maps
User-created basemaps
2 parents 569963d + f77b94a commit 7971b5c

File tree

17 files changed

+499
-62
lines changed

17 files changed

+499
-62
lines changed

geoinsight/core/admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.contrib import admin
22

33
from geoinsight.core.models import (
4+
Basemap,
45
Chart,
56
ColorConfig,
67
Colormap,
@@ -25,6 +26,11 @@
2526
)
2627

2728

29+
@admin.register(Basemap)
30+
class BasemapAdmin(admin.ModelAdmin):
31+
list_display = ['id', 'name']
32+
33+
2834
@admin.register(Project)
2935
class ProjectAdmin(admin.ModelAdmin):
3036
list_display = ['id', 'name']
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Generated by Django 5.2.8 on 2025-12-10 16:19
2+
3+
import json
4+
from pathlib import Path
5+
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
9+
10+
def forwards(apps, schema_editor):
11+
basemap_model = apps.get_model('core', 'Basemap')
12+
default_basemaps_file = Path(settings.BASE_DIR, 'geoinsight', 'core', 'models', 'basemaps.json')
13+
with open(default_basemaps_file) as f:
14+
default_basemaps = json.load(f)
15+
for default_basemap in default_basemaps:
16+
name = default_basemap.get('name')
17+
style = default_basemap.get('style')
18+
if name is not None and style is not None:
19+
basemap_model.objects.create(
20+
name=name,
21+
style=style,
22+
)
23+
24+
25+
class Migration(migrations.Migration):
26+
27+
dependencies = [
28+
('core', '0016_dataset_owner_and_tags'),
29+
]
30+
31+
operations = [
32+
migrations.CreateModel(
33+
name='Basemap',
34+
fields=[
35+
(
36+
'id',
37+
models.BigAutoField(
38+
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
39+
),
40+
),
41+
('name', models.CharField(max_length=100)),
42+
('style', models.JSONField()),
43+
],
44+
),
45+
migrations.RunPython(forwards, migrations.RunPython.noop),
46+
]

geoinsight/core/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .basemap import Basemap
12
from .chart import Chart
23
from .colormap import Colormap
34
from .data import RasterData, VectorData, VectorFeature
@@ -18,6 +19,7 @@
1819
from .task_result import TaskResult
1920

2021
__all__ = [
22+
'Basemap',
2123
'TaskResult',
2224
'Chart',
2325
'Colormap',

geoinsight/core/models/basemap.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from django.db import models
2+
3+
4+
class Basemap(models.Model):
5+
name = models.CharField(max_length=100)
6+
style = models.JSONField()
7+
8+
def __str__(self):
9+
return f'{self.name} ({self.id})'
10+
11+
@classmethod
12+
def filter_queryset_by_projects(cls, queryset, projects):
13+
# Basemap permissions are not determined by Project permissions
14+
return queryset
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
[
2+
{
3+
"name": "Basic Light",
4+
"style": "https://demo.kitware.com/vector-maps/tiles/style/openstreetmap-openmaptiles-openfreemap-positron.json"
5+
},
6+
{
7+
"name": "Basic Dark",
8+
"style": "https://demo.kitware.com/vector-maps/tiles/style/openstreetmap-openmaptiles-openmaptiles-dark-matter.json"
9+
},
10+
{
11+
"name": "NAIP Imagery",
12+
"style": {
13+
"version": 8,
14+
"name": "NAIP",
15+
"sources": {
16+
"naip": {
17+
"type": "raster",
18+
"tiles": [
19+
"https://gis.apfo.usda.gov/arcgis/rest/services/NAIP/USDA_CONUS_PRIME/ImageServer/tile/{z}/{y}/{x}?blankTile=false"
20+
],
21+
"tileSize": 256
22+
}
23+
},
24+
"layers": [
25+
{
26+
"id": "naip-tiles",
27+
"type": "raster",
28+
"source": "naip"
29+
}
30+
]
31+
}
32+
},
33+
{
34+
"name": "OSM",
35+
"style": {
36+
"version": 8,
37+
"sources": {
38+
"osm": {
39+
"type": "raster",
40+
"tiles": [
41+
"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
42+
],
43+
"tileSize": 256,
44+
"maxzoom": 19
45+
}
46+
},
47+
"layers": [
48+
{
49+
"id": "osm",
50+
"type": "raster",
51+
"source": "osm"
52+
}
53+
]
54+
}
55+
},
56+
{
57+
"name": "ArcGIS Hybrid",
58+
"style": "https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/arcgis_hybrid.json"
59+
},
60+
{
61+
"name": "CartoCDN Voyager",
62+
"style": "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
63+
},
64+
{
65+
"name": "ICGC Dark",
66+
"style": "https://geoserveis.icgc.cat/contextmaps/icgc_mapa_base_fosc.json"
67+
},
68+
{
69+
"name": "ICGC Dark Shadow",
70+
"style": "https://geoserveis.icgc.cat/contextmaps/icgc_ombra_fosca.json"
71+
},
72+
{
73+
"name": "ICGC Standard Orthophoto",
74+
"style": "https://geoserveis.icgc.cat/contextmaps/icgc_orto_estandard.json"
75+
},
76+
{
77+
"name": "ICGC Standard Orthophoto Gray",
78+
"style": "https://geoserveis.icgc.cat/contextmaps/icgc_orto_estandard_gris.json"
79+
},
80+
{
81+
"name": "ICGC Hybrid Orthophoto",
82+
"style": "https://geoserveis.icgc.cat/contextmaps/icgc_orto_hibrida.json"
83+
}
84+
]

geoinsight/core/rest/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .analytics import AnalyticsViewSet
2+
from .basemap import BasemapViewSet
23
from .chart import ChartViewSet
34
from .colormap import ColormapViewSet
45
from .data import RasterDataViewSet, VectorDataViewSet
@@ -12,6 +13,7 @@
1213

1314
__all__ = [
1415
'AnalyticsViewSet',
16+
'BasemapViewSet',
1517
'ChartViewSet',
1618
'ColormapViewSet',
1719
'LayerViewSet',

geoinsight/core/rest/basemap.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from rest_framework.viewsets import ModelViewSet
2+
3+
from geoinsight.core.models import Basemap
4+
from geoinsight.core.rest.access_control import GuardianFilter, GuardianPermission
5+
from geoinsight.core.rest.serializers import BasemapSerializer
6+
7+
8+
class BasemapViewSet(ModelViewSet):
9+
queryset = Basemap.objects.all()
10+
serializer_class = BasemapSerializer
11+
permission_classes = [GuardianPermission]
12+
filter_backends = [GuardianFilter]
13+
lookup_field = 'id'

geoinsight/core/rest/serializers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from rest_framework import serializers
55

66
from geoinsight.core.models import (
7+
Basemap,
78
Chart,
89
Colormap,
910
Dataset,
@@ -29,6 +30,12 @@ class Meta:
2930
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_superuser']
3031

3132

33+
class BasemapSerializer(serializers.ModelSerializer):
34+
class Meta:
35+
model = Basemap
36+
fields = '__all__'
37+
38+
3239
class ProjectPermissionsSerializer(serializers.Serializer):
3340
owner_id = serializers.IntegerField()
3441
collaborator_ids = serializers.ListField(child=serializers.IntegerField())

geoinsight/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from geoinsight.core.rest import (
1111
AnalyticsViewSet,
12+
BasemapViewSet,
1213
ChartViewSet,
1314
ColormapViewSet,
1415
DatasetViewSet,
@@ -45,6 +46,7 @@
4546
router.register(r'vectors', VectorDataViewSet, basename='vectors')
4647
router.register(r'source-regions', RegionViewSet, basename='source-regions')
4748
router.register(r'networks', NetworkViewSet, basename='networks')
49+
router.register(r'basemaps', BasemapViewSet, basename='basemaps')
4850
router.register(r'analytics', AnalyticsViewSet, basename='analytics')
4951

5052

web/src/api/rest.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,21 @@ import {
2020
LayerFrame,
2121
TaskResult,
2222
Colormap,
23+
Basemap,
2324
} from "@/types";
2425

2526
export async function getUsers(): Promise<User[]> {
2627
return (await apiClient.get('users/')).data.results;
2728
}
2829

30+
export async function getBasemaps(): Promise<Basemap[]> {
31+
return (await apiClient.get('basemaps/')).data.results;
32+
}
33+
34+
export async function createBasemap(basemap: Basemap): Promise<Basemap> {
35+
return (await apiClient.post('basemaps/', basemap)).data;
36+
}
37+
2938
export async function getProjects(): Promise<Project[]> {
3039
return (await apiClient.get('projects/')).data.results;
3140
}

0 commit comments

Comments
 (0)