-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #117 from arthur-schnitzler/main
cool 3d calender
- Loading branch information
Showing
12 changed files
with
530 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
function dateToCoordinates(isoDate, minYear = 1500, maxYear = 1800) { | ||
// Parse the ISO date | ||
const date = new Date(isoDate); | ||
if (isNaN(date)) { | ||
throw new Error("Invalid date format"); | ||
} | ||
|
||
// Extract year, month, and day | ||
const year = date.getUTCFullYear(); | ||
const month = date.getUTCMonth() + 1; // Months are 0-based | ||
const day = date.getUTCDate(); | ||
|
||
// Map year to latitude (-90 to 90) with larger gaps between years | ||
const yearFactor = (year - minYear) / (maxYear - minYear); | ||
const latitude = yearFactor * 180 - 90; | ||
|
||
// Map month and day to longitude (-180 to 180) | ||
const maxMonth = 12; | ||
const maxDay = 31; // Approximation for simplicity | ||
const monthFactor = (month - 1) / (maxMonth - 1); | ||
const dayFactor = (day - 1) / (maxDay - 1); | ||
const longitude = ((monthFactor + dayFactor) / 2) * 360 - 180; | ||
|
||
return { lat: latitude, lng: longitude }; | ||
} | ||
|
||
function scaleToRange( | ||
x, | ||
originalMin = 1, | ||
originalMax = 10, | ||
targetMin = 0, | ||
targetMax = 250 | ||
) { | ||
return ( | ||
Math.round( | ||
((x - originalMin) * (targetMax - targetMin)) / | ||
(originalMax - originalMin) | ||
) + targetMin | ||
); | ||
} | ||
|
||
function mapValueToColor(value) { | ||
if (value < 1 || value > 10) { | ||
return [0, 0, 0]; | ||
} | ||
|
||
// Normalize the value to a range of 0 to 1 | ||
const normalized = (value - 1) / 9; | ||
|
||
// Define the gradient colors (red -> yellow -> green) | ||
const startColor = [255, 0, 0]; // Red | ||
const midColor = [255, 255, 0]; // Yellow | ||
const endColor = [0, 255, 0]; // Green | ||
|
||
let color; | ||
if (normalized <= 0.5) { | ||
// Interpolate between startColor and midColor | ||
const t = normalized * 2; // Scale to [0, 1] | ||
color = startColor.map((start, i) => | ||
Math.round(start + t * (midColor[i] - start)) | ||
); | ||
} else { | ||
// Interpolate between midColor and endColor | ||
const t = (normalized - 0.5) * 2; // Scale to [0, 1] | ||
color = midColor.map((mid, i) => Math.round(mid + t * (endColor[i] - mid))); | ||
} | ||
|
||
// Convert to RGB format | ||
return [color[0], color[1], color[2]]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
const url = document.getElementById("url").textContent; | ||
console.log("fetching data"); | ||
fetch(url) | ||
.then((response) => { | ||
if (!response.ok) { | ||
throw new Error("Calender data response was not ok"); | ||
} | ||
return response.json(); | ||
}) | ||
.then((data) => { | ||
const legendDiv = document.getElementById("legend"); | ||
const dl = document.createElement("dl"); // Create the <dl> element | ||
|
||
data.metadata.query_params.forEach((param) => { | ||
for (const [key, value] of Object.entries(param)) { | ||
const dt = document.createElement("dt"); // Create the <dt> element | ||
dt.textContent = key; | ||
const dd = document.createElement("dd"); // Create the <dd> element | ||
dd.textContent = value; | ||
|
||
dl.appendChild(dt); | ||
dl.appendChild(dd); | ||
} | ||
}); | ||
|
||
// Ensure each event has a label property | ||
const validEvents = data.events | ||
.filter((event) => event.latitude && event.longitude) | ||
.map((event) => ({ | ||
...event, | ||
label: event.label || "Unknown Event", // Default to 'Unknown Event' if label is missing | ||
})); | ||
|
||
console.log(validEvents); | ||
const deckgl = new deck.DeckGL({ | ||
container: "map", | ||
initialViewState: { | ||
altitude: 1.5, | ||
height: 700, | ||
longitude: 80, | ||
latitude: 35, | ||
zoom: 2, | ||
pitch: 60, | ||
// bearing: -1.7 | ||
}, | ||
controller: true, | ||
// onViewStateChange: ({ viewState }) => { | ||
// console.log("Current view state:", viewState); | ||
// }, | ||
layers: [ | ||
new deck.HexagonLayer({ | ||
data: validEvents, | ||
getPosition: (d) => [d.longitude, d.latitude], | ||
radius: 50000, | ||
elevationScale: 4000, | ||
elevationRange: [0, 50], | ||
extruded: true, | ||
pickable: true, | ||
onHover: ({ object, x, y }) => { | ||
const tooltip = document.getElementById("tooltip"); | ||
if (object) { | ||
const eventLabels = object.points.map((p) => p.source.label); | ||
const curDate = object.points[0].source.date; | ||
const listItems = eventLabels | ||
.map((label) => `<li>${label}</li>`) | ||
.join(""); | ||
tooltip.style.display = "block"; | ||
tooltip.style.left = `${x}px`; | ||
tooltip.style.top = `${y}px`; | ||
tooltip.innerHTML = `<strong>${curDate}</strong><ul>${listItems}</ul>`; | ||
} else { | ||
tooltip.style.display = "none"; | ||
} | ||
}, | ||
}), | ||
], | ||
}); | ||
|
||
legendDiv.appendChild(dl); | ||
}) | ||
.catch((error) => { | ||
console.error("Something went wrong:", error); | ||
}); | ||
|
||
// Add this CSS for the tooltip | ||
const style = document.createElement("style"); | ||
style.innerHTML = ` | ||
#tooltip { | ||
position: absolute; | ||
background: white; | ||
padding: 5px; | ||
border: 1px solid black; | ||
display: none; | ||
pointer-events: none; | ||
} | ||
`; | ||
document.head.appendChild(style); | ||
|
||
// Add this HTML for the tooltip | ||
const tooltip = document.createElement("div"); | ||
tooltip.id = "tooltip"; | ||
document.body.appendChild(tooltip); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
const url = document.getElementById("url").textContent; | ||
console.log("fetching data"); | ||
fetch(url) | ||
.then((response) => { | ||
if (!response.ok) { | ||
throw new Error("Geojson response was not ok"); | ||
} | ||
return response.json(); | ||
}) | ||
.then((data) => { | ||
var map = L.map("map"); | ||
const legendDiv = document.getElementById("legend"); | ||
const dl = document.createElement("dl"); // Create the <dl> element | ||
|
||
data.metadata.query_params.forEach((param) => { | ||
for (const [key, value] of Object.entries(param)) { | ||
const dt = document.createElement("dt"); // Create the <dt> element | ||
dt.textContent = key; | ||
const dd = document.createElement("dd"); // Create the <dd> element | ||
dd.textContent = value; | ||
|
||
dl.appendChild(dt); // Append <dt> to <dl> | ||
dl.appendChild(dd); // Append <dd> to <dl> | ||
} | ||
}); | ||
|
||
legendDiv.appendChild(dl); | ||
var OSMBaseLayer = L.tileLayer( | ||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", | ||
{ | ||
maxZoom: 19, | ||
attribution: | ||
'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>', | ||
} | ||
).addTo(map); | ||
|
||
var CartoDB_PositronNoLabels = L.tileLayer( | ||
"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png", | ||
{ | ||
attribution: | ||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>', | ||
subdomains: "abcd", | ||
maxZoom: 20, | ||
} | ||
); | ||
|
||
var CartoDB_DarkMatterNoLabels = L.tileLayer( | ||
"https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png", | ||
{ | ||
attribution: | ||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>', | ||
subdomains: "abcd", | ||
maxZoom: 20, | ||
} | ||
); | ||
|
||
const markers = L.markerClusterGroup(); | ||
const geojsonLayer = L.geoJSON(data, { | ||
onEachFeature: function (feature, layer) { | ||
layer.bindPopup(feature.properties.label); | ||
}, | ||
pointToLayer: function (feature, latlng) { | ||
return L.marker(latlng); | ||
}, | ||
}); | ||
markers.addTo(map); | ||
geojsonLayer.eachLayer((layer) => markers.addLayer(layer)); | ||
|
||
var heatData = []; | ||
L.geoJSON(data, { | ||
onEachFeature: function (feature, layer) { | ||
if (feature.geometry.type === "Point") { | ||
var lat = feature.geometry.coordinates[1]; | ||
var lng = feature.geometry.coordinates[0]; | ||
heatData.push([lat, lng]); | ||
} | ||
}, | ||
}); | ||
|
||
// Create the heatmap layer | ||
var heatmapLayer = L.heatLayer(heatData, { | ||
radius: 25, | ||
blur: 10, | ||
maxZoom: 17, | ||
max: 0.7, | ||
gradient: { 0: "white", 0.5: "lime", 1: "red" }, | ||
}); | ||
|
||
var baseMaps = { | ||
"Base Layer": OSMBaseLayer, | ||
"CartoDB hell": CartoDB_PositronNoLabels, | ||
"CartoDB dunkel": CartoDB_DarkMatterNoLabels, | ||
}; | ||
|
||
const overlayMaps = { | ||
"Marker Cluster": markers, | ||
Heatmap: heatmapLayer, | ||
}; | ||
|
||
L.control.layers(baseMaps, overlayMaps, { collapsed: false }).addTo(map); | ||
map.fitBounds(geojsonLayer.getBounds()); | ||
}) | ||
.catch((error) => { | ||
console.error("Something went wrong:", error); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{% extends "base.html" %} | ||
{% load static %} | ||
{% block title %}Map{% endblock %} | ||
{% block scriptHeader %} | ||
{% endblock %} | ||
{% block content %} | ||
<style> | ||
#map { | ||
width: 100%; | ||
height: 700px; | ||
} | ||
</style> | ||
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script> | ||
<div class="container-fluid pt-3"> | ||
<h1 class="display-3 text-center">Kalender</h1> | ||
<div id="mapcontainer" class="p-4"> | ||
<div id="map"></div> | ||
<div id="legend" class="text-center pt-3"> | ||
<h2>gewählte Filterparameter</h2> | ||
</div> | ||
</div> | ||
</div> | ||
<span id="url" class="visually-hidden" aria-hidden="true">{% url 'network:calender_data' %}{% querystring %}</span> | ||
|
||
<script src="{% static 'network/calender.js' %}"></script> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.