mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-04 12:45:17 +02:00
chore: Add GeoJSON endpoint to retrieve combined GeoJSON data from static files
This commit is contained in:
parent
70d08eff8a
commit
57eee7bb5d
4 changed files with 83 additions and 25 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country
|
from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, GeoJSONView
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'countries', CountryViewSet, basename='countries')
|
router.register(r'countries', CountryViewSet, basename='countries')
|
||||||
|
@ -13,4 +13,5 @@ urlpatterns = [
|
||||||
path('', include(router.urls)),
|
path('', include(router.urls)),
|
||||||
path('<str:country_code>/regions/', regions_by_country, name='regions-by-country'),
|
path('<str:country_code>/regions/', regions_by_country, name='regions-by-country'),
|
||||||
path('<str:country_code>/visits/', visits_by_country, name='visits-by-country'),
|
path('<str:country_code>/visits/', visits_by_country, name='visits-by-country'),
|
||||||
|
path('geojson/', GeoJSONView.as_view({'get': 'list'}), name='geojson'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,10 @@ from rest_framework.permissions import IsAuthenticated
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.staticfiles import finders
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
|
@ -50,3 +54,37 @@ class VisitedRegionViewSet(viewsets.ModelViewSet):
|
||||||
self.perform_create(serializer)
|
self.perform_create(serializer)
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
class GeoJSONView(viewsets.ViewSet):
|
||||||
|
"""
|
||||||
|
Combine all GeoJSON data from .json files in static/data into a single GeoJSON object.
|
||||||
|
"""
|
||||||
|
def list(self, request):
|
||||||
|
combined_geojson = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use Django's static file finder to locate the 'data' directory
|
||||||
|
data_dir = finders.find('data')
|
||||||
|
|
||||||
|
if not data_dir or not os.path.isdir(data_dir):
|
||||||
|
return Response({"error": "Data directory does not exist."}, status=404)
|
||||||
|
|
||||||
|
for filename in os.listdir(data_dir):
|
||||||
|
if filename.endswith('.json'):
|
||||||
|
file_path = os.path.join(data_dir, filename)
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
json_data = json.load(f)
|
||||||
|
# Check if the JSON data is GeoJSON
|
||||||
|
if isinstance(json_data, dict) and "type" in json_data:
|
||||||
|
if json_data["type"] == "FeatureCollection":
|
||||||
|
combined_geojson["features"].extend(json_data.get("features", []))
|
||||||
|
elif json_data["type"] == "Feature":
|
||||||
|
combined_geojson["features"].append(json_data)
|
||||||
|
# You can add more conditions here for other GeoJSON types if needed
|
||||||
|
except (IOError, json.JSONDecodeError) as e:
|
||||||
|
return Response({"error": f"Error reading file {filename}: {str(e)}"}, status=500)
|
||||||
|
|
||||||
|
return Response(combined_geojson)
|
|
@ -5,12 +5,6 @@ import type { Adventure, VisitedRegion } from '$lib/types';
|
||||||
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
export const load = (async (event) => {
|
export const load = (async (event) => {
|
||||||
let countryCodesToFetch = ['FR', 'US', 'CA', 'DE', 'AU', 'MX', 'JP'];
|
|
||||||
let geoJSON = {
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
features: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
return redirect(302, '/login');
|
return redirect(302, '/login');
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,6 +14,8 @@ export const load = (async (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let geoJsonUrl = `${endpoint}/api/geojson/` as string;
|
||||||
|
|
||||||
let visitedRegionsFetch = await fetch(`${endpoint}/api/visitedregion/`, {
|
let visitedRegionsFetch = await fetch(`${endpoint}/api/visitedregion/`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `${event.cookies.get('auth')}`
|
Cookie: `${event.cookies.get('auth')}`
|
||||||
|
@ -27,19 +23,6 @@ export const load = (async (event) => {
|
||||||
});
|
});
|
||||||
let visitedRegions = (await visitedRegionsFetch.json()) as VisitedRegion[];
|
let visitedRegions = (await visitedRegionsFetch.json()) as VisitedRegion[];
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
countryCodesToFetch.map(async (code) => {
|
|
||||||
let res = await fetch(`${endpoint}/static/data/${code.toLowerCase()}.json`);
|
|
||||||
console.log('fetching ' + code);
|
|
||||||
let json = await res.json();
|
|
||||||
if (!json) {
|
|
||||||
console.error(`Failed to fetch ${code} GeoJSON`);
|
|
||||||
} else {
|
|
||||||
geoJSON.features = geoJSON.features.concat(json.features);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!visitedFetch.ok) {
|
if (!visitedFetch.ok) {
|
||||||
console.error('Failed to fetch visited adventures');
|
console.error('Failed to fetch visited adventures');
|
||||||
return redirect(302, '/login');
|
return redirect(302, '/login');
|
||||||
|
@ -61,7 +44,7 @@ export const load = (async (event) => {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
markers,
|
markers,
|
||||||
geoJSON,
|
geoJsonUrl,
|
||||||
visitedRegions
|
visitedRegions
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,24 @@
|
||||||
|
|
||||||
let clickedName = '';
|
let clickedName = '';
|
||||||
|
|
||||||
|
let showVisited = true;
|
||||||
|
let showPlanned = true;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (!showVisited) {
|
||||||
|
markers = markers.filter((marker) => marker.type !== 'visited');
|
||||||
|
} else {
|
||||||
|
const visitedMarkers = data.props.markers.filter((marker) => marker.type === 'visited');
|
||||||
|
markers = [...markers, ...visitedMarkers];
|
||||||
|
}
|
||||||
|
if (!showPlanned) {
|
||||||
|
markers = markers.filter((marker) => marker.type !== 'planned');
|
||||||
|
} else {
|
||||||
|
const plannedMarkers = data.props.markers.filter((marker) => marker.type === 'planned');
|
||||||
|
markers = [...markers, ...plannedMarkers];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let newMarker = [];
|
let newMarker = [];
|
||||||
|
|
||||||
let newLongitude = null;
|
let newLongitude = null;
|
||||||
|
@ -61,7 +79,7 @@
|
||||||
|
|
||||||
let visitedRegions = data.props.visitedRegions;
|
let visitedRegions = data.props.visitedRegions;
|
||||||
|
|
||||||
let geoJSON = data.props.geoJSON;
|
let geoJSON = [];
|
||||||
|
|
||||||
let visitArray = [];
|
let visitArray = [];
|
||||||
|
|
||||||
|
@ -77,11 +95,29 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapped to the checkbox
|
// mapped to the checkbox
|
||||||
let showGEO = true;
|
let showGEO = false;
|
||||||
|
$: {
|
||||||
|
if (showGEO && geoJSON.length === 0) {
|
||||||
|
(async () => {
|
||||||
|
geoJSON = await fetch(data.props.geoJsonUrl).then((res) => res.json());
|
||||||
|
})();
|
||||||
|
} else if (!showGEO) {
|
||||||
|
geoJSON = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let createModalOpen = false;
|
let createModalOpen = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<label class="label cursor-pointer">
|
||||||
|
<span class="label-text">Visited</span>
|
||||||
|
<input type="checkbox" bind:checked={showVisited} class="checkbox checkbox-primary" />
|
||||||
|
</label>
|
||||||
|
<label class="label cursor-pointer">
|
||||||
|
<span class="label-text">Planned</span>
|
||||||
|
<input type="checkbox" bind:checked={showPlanned} class="checkbox checkbox-primary" />
|
||||||
|
</label>
|
||||||
|
|
||||||
{#if newMarker.length > 0}
|
{#if newMarker.length > 0}
|
||||||
<button type="button" class="btn btn-primary mb-2" on:click={() => (createModalOpen = true)}
|
<button type="button" class="btn btn-primary mb-2" on:click={() => (createModalOpen = true)}
|
||||||
>Add New Adventure at Marker</button
|
>Add New Adventure at Marker</button
|
||||||
|
@ -156,7 +192,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#if showGEO}
|
{#if showGEO}
|
||||||
<GeoJSON id="states" data={data.props.geoJSON} promoteId="ISOCODE">
|
<GeoJSON id="states" data={geoJSON} promoteId="ISOCODE">
|
||||||
<LineLayer
|
<LineLayer
|
||||||
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
|
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
|
||||||
paint={{ 'line-color': 'grey', 'line-width': 3 }}
|
paint={{ 'line-color': 'grey', 'line-width': 3 }}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue