1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-04 04:35:19 +02:00

chore: Add GeoJSON endpoint to retrieve combined GeoJSON data from static files

This commit is contained in:
Sean Morley 2024-07-27 12:46:50 -04:00
parent 70d08eff8a
commit 57eee7bb5d
4 changed files with 83 additions and 25 deletions

View file

@ -2,7 +2,7 @@
from django.urls import include, path
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.register(r'countries', CountryViewSet, basename='countries')
@ -13,4 +13,5 @@ urlpatterns = [
path('', include(router.urls)),
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('geojson/', GeoJSONView.as_view({'get': 'list'}), name='geojson'),
]

View file

@ -6,6 +6,10 @@ from rest_framework.permissions import IsAuthenticated
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
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'])
@permission_classes([IsAuthenticated])
@ -50,3 +54,37 @@ class VisitedRegionViewSet(viewsets.ModelViewSet):
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
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)

View file

@ -5,12 +5,6 @@ import type { Adventure, VisitedRegion } from '$lib/types';
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const load = (async (event) => {
let countryCodesToFetch = ['FR', 'US', 'CA', 'DE', 'AU', 'MX', 'JP'];
let geoJSON = {
type: 'FeatureCollection',
features: []
};
if (!event.locals.user) {
return redirect(302, '/login');
} else {
@ -20,6 +14,8 @@ export const load = (async (event) => {
}
});
let geoJsonUrl = `${endpoint}/api/geojson/` as string;
let visitedRegionsFetch = await fetch(`${endpoint}/api/visitedregion/`, {
headers: {
Cookie: `${event.cookies.get('auth')}`
@ -27,19 +23,6 @@ export const load = (async (event) => {
});
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) {
console.error('Failed to fetch visited adventures');
return redirect(302, '/login');
@ -61,7 +44,7 @@ export const load = (async (event) => {
return {
props: {
markers,
geoJSON,
geoJsonUrl,
visitedRegions
}
};

View file

@ -17,6 +17,24 @@
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 newLongitude = null;
@ -61,7 +79,7 @@
let visitedRegions = data.props.visitedRegions;
let geoJSON = data.props.geoJSON;
let geoJSON = [];
let visitArray = [];
@ -77,11 +95,29 @@
}
// 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;
</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}
<button type="button" class="btn btn-primary mb-2" on:click={() => (createModalOpen = true)}
>Add New Adventure at Marker</button
@ -156,7 +192,7 @@
{/if}
{/each}
{#if showGEO}
<GeoJSON id="states" data={data.props.geoJSON} promoteId="ISOCODE">
<GeoJSON id="states" data={geoJSON} promoteId="ISOCODE">
<LineLayer
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
paint={{ 'line-color': 'grey', 'line-width': 3 }}