Vis sogne / provsti
Denne side gøre det muligt at downloade sognet figur i png/svg format. Virker kun i Sandbox pga Churchdesk
Boksen overnover gør det muligt at finde figuerer på sogen og provstier udelukkende for sognekoder, evt. adskilt af komma
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Sogn Finder med Kort</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
body {
font-family: "Segoe UI", sans-serif;
}
.sogn-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
position: relative;
}
input, button {
width: 100%;
padding: 10px;
margin-top: 10px;
}
/* ✅ STANDARD KNAPPER */
button {
background-color: #003366;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
transition: all 0.2s ease;
}
button:hover {
background-color: #0055aa;
}
/* ✅ KUN "Find sogn" */
#sognForm button {
color: black;
box-shadow: 0 6px 12px rgba(0,0,0,0.4);
}
#sognForm button:hover {
transform: translateY(-2px);
}
#sognForm button:active {
transform: translateY(1px);
}
#dropdown {
width: 100%;
display: none;
border: 1px solid #ccc;
background: white;
position: absolute;
z-index: 1000;
max-height: 200px;
overflow-y: auto;
}
.dropdown-item {
padding: 10px;
cursor: pointer;
}
.dropdown-item:hover {
background-color: #ddd;
}
#map {
height: 400px;
margin-top: 20px;
display: none;
}
#downloadButtons {
display: none;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="sogn-container">
<h2>Find dit sogn</h2>
<form id="sognForm">
<label>Indtast din adresse:</label>
<input type="text" id="address" autocomplete="off" />
<div id="dropdown"></div>
<button type="submit">Find sogn</button>
</form>
<div id="result"></div>
<div id="map"></div>
<div id="downloadButtons">
<button onclick="downloadSVG()">Download SVG</button>
<button onclick="downloadPNG()">Download PNG</button>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const addressInput = document.getElementById("address");
const dropdown = document.getElementById("dropdown");
const mapContainer = document.getElementById("map");
let map;
let geoLayer;
/* 🔍 AUTOCOMPLETE */
addressInput.addEventListener("input", async function () {
const query = this.value;
if (query.length < 3) {
dropdown.style.display = "none";
return;
}
const res = await fetch(`https://api.dataforsyningen.dk/adresser/autocomplete?q=${encodeURIComponent(query)}`);
const data = await res.json();
dropdown.innerHTML = "";
dropdown.style.display = "block";
data.forEach(item => {
const div = document.createElement("div");
div.classList.add("dropdown-item");
div.innerText = item.tekst;
div.onclick = () => {
addressInput.value = item.tekst;
dropdown.style.display = "none";
};
dropdown.appendChild(div);
});
});
/* ✅ FIND SOGN */
document.getElementById("sognForm").addEventListener("submit", async function (e) {
e.preventDefault();
const address = addressInput.value;
const addrRes = await fetch(`https://api.dataforsyningen.dk/adresser?q=${encodeURIComponent(address)}`);
const addrData = await addrRes.json();
if (!addrData.length) return alert("Adresse ikke fundet");
const [x, y] = addrData[0].adgangsadresse.adgangspunkt.koordinater;
const sognRes = await fetch(`https://api.dataforsyningen.dk/sogne?x=${x}&y=${y}`);
const sognData = await sognRes.json();
if (!sognData.length) return alert("Sogn ikke fundet");
document.getElementById("result").innerText = "Sogn: " + sognData[0].navn;
visKort(sognData[0].kode);
});
/* 🗺️ VIS KORT */
function visKort(kode) {
const url = `https://api.dataforsyningen.dk/sogne/${kode}?format=geojson`;
mapContainer.style.display = "block";
document.getElementById("downloadButtons").style.display = "block";
if (!map) {
map = L.map("map");
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")
.addTo(map);
}
if (geoLayer) geoLayer.remove();
fetch(url)
.then(res => res.json())
.then(data => {
geoLayer = L.geoJSON(data).addTo(map);
map.fitBounds(geoLayer.getBounds());
});
}
/* ✅ SVG */
function downloadSVG() {
const svg = document.querySelector("#map svg");
if (!svg) return alert("SVG ikke klar");
const blob = new Blob(
[new XMLSerializer().serializeToString(svg)],
{ type: "image/svg+xml" }
);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "sogn.svg";
a.click();
}
/* ✅ PNG – korrekt projektion */
function downloadPNG() {
if (!geoLayer) return alert("Ingen sogn valgt");
const geojson = geoLayer.toGeoJSON();
const size = 2000;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, size, size);
function project(lat, lng) {
return L.CRS.EPSG3857.project(L.latLng(lat, lng));
}
let rings = [];
geojson.features.forEach(f => {
f.geometry.coordinates.forEach(poly => {
poly.forEach(ring => {
rings.push(ring.map(c => project(c[1], c[0])));
});
});
});
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
rings.flat().forEach(p => {
if (p.x < minX) minX = p.x;
if (p.x > maxX) maxX = p.x;
if (p.y < minY) minY = p.y;
if (p.y > maxY) maxY = p.y;
});
const spanX = maxX - minX;
const spanY = maxY - minY;
const scale = Math.min(size / spanX, size / spanY);
const offsetX = (size - spanX * scale) / 2;
const offsetY = (size - spanY * scale) / 2;
ctx.fillStyle = "#000";
ctx.strokeStyle = "#000";
ctx.lineWidth = 3;
rings.forEach(ring => {
ctx.beginPath();
ring.forEach((p, i) => {
const x = (p.x - minX) * scale + offsetX;
const y = (maxY - p.y) * scale + offsetY;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.closePath();
ctx.fill();
ctx.stroke();
});
const link = document.createElement("a");
link.download = "sogn.png";
link.href = canvas.toDataURL("image/png");
link.click();
}
</script>
</body>
</html>
Koden herunder er til flere sogne adskilt af komma
<div class="sogn-wrapper">
<div class="sogn-content">
<h2>Vis sogne / provsti</h2>
<input id="codes" placeholder="fx 7068, 7070">
<button onclick="loadCodes()">Vis</button>
<div id="map"></div>
<div id="downloads">
<button onclick="downloadSVG()">Download SVG</button>
<button onclick="downloadPNG()">Download PNG</button>
</div>
</div>
</div>
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
/* ✅ BRYD HÅRDT UD AF CHURCHDESK */
.sogn-wrapper {
position: relative;
width: 100vw;
left: 50%;
transform: translateX(-50%);
}
/* ✅ INDHOLD */
.sogn-content {
max-width: 1100px;
margin: auto;
padding: 20px;
box-sizing: border-box;
font-family: "Segoe UI", sans-serif;
}
/* ✅ input + knapper */
input, button {
width: 100%;
padding: 10px;
margin-top: 10px;
}
button {
background: #003366;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
}
/* ✅ kort */
#map {
width: 100%;
height: 500px;
margin-top: 20px;
display: none;
border-radius: 8px;
}
#downloads {
display: none;
margin-top: 10px;
}
h2 {
text-align: center;
}
</style>
<script>
let map;
let layers = [];
async function loadCodes() {
const input = document.getElementById("codes").value;
if (!input) return;
const codes = input.split(",").map(c => c.trim());
document.getElementById("map").style.display = "block";
document.getElementById("downloads").style.display = "block";
if (!map) {
map = L.map("map");
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")
.addTo(map);
}
layers.forEach(l => map.removeLayer(l));
layers = [];
let bounds;
for (const code of codes) {
let url = `https://api.dataforsyningen.dk/sogne/${code}?format=geojson`;
let res = await fetch(url);
if (!res.ok) {
url = `https://api.dataforsyningen.dk/provstier/${code}?format=geojson`;
res = await fetch(url);
}
if (!res.ok) continue;
const data = await res.json();
const layer = L.geoJSON(data).addTo(map);
layers.push(layer);
if (!bounds) bounds = layer.getBounds();
else bounds.extend(layer.getBounds());
}
if (bounds) map.fitBounds(bounds);
}
/* ✅ SVG */
function downloadSVG() {
const svg = document.querySelector("#map svg");
if (!svg) return alert("Kortet er ikke klar endnu");
const clone = svg.cloneNode(true);
clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
const blob = new Blob(
[new XMLSerializer().serializeToString(clone)],
{ type: "image/svg+xml" }
);
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "områder.svg";
link.click();
}
/* ✅ PNG */
function downloadPNG() {
if (!layers.length) return alert("Ingen data");
const size = 2000;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, size, size);
function project(lat, lng) {
return L.CRS.EPSG3857.project(L.latLng(lat, lng));
}
let rings = [];
layers.forEach(layer => {
const geo = layer.toGeoJSON();
geo.features.forEach(f => {
f.geometry.coordinates.forEach(poly => {
poly.forEach(r => {
rings.push(r.map(c => project(c[1], c[0])));
});
});
});
});
let minX=Infinity,maxX=-Infinity,minY=Infinity,maxY=-Infinity;
rings.flat().forEach(p => {
if (p.x < minX) minX = p.x;
if (p.x > maxX) maxX = p.x;
if (p.y < minY) minY = p.y;
if (p.y > maxY) maxY = p.y;
});
const scale = Math.min(size/(maxX-minX), size/(maxY-minY));
const offsetX = (size-(maxX-minX)*scale)/2;
const offsetY = (size-(maxY-minY)*scale)/2;
ctx.fillStyle = "#000";
rings.forEach(ring => {
ctx.beginPath();
ring.forEach((p,i)=>{
const x = (p.x-minX)*scale + offsetX;
const y = (maxY-p.y)*scale + offsetY;
if(i===0) ctx.moveTo(x,y);
else ctx.lineTo(x,y);
});
ctx.closePath();
ctx.fill();
});
const link = document.createElement("a");
link.download = "områder.png";
link.href = canvas.toDataURL();
link.click();
}
</script>