Anleitung

Ihr Liefergebiet auf einer Karte

Für Pizzerien, Läden mit Lieferdienst und Handwerksbetriebe mit Einsatzgebiet: Zonen um Ihr Geschäft mit der Liefergebühr jeder Zone, oder ein Gebiet, das Sie selbst zeichnen, und eine Antwort für jeden Ort, den die Kundschaft anklickt.

Klicken Sie auf der Karte dorthin, wo Sie Ihre Pizza möchten.
Die Karte wird geladen…
Der Code unten, mit einer Pizzeria in Bern, die es nicht gibt.

Drei Schritte

  1. Erstellen Sie ein kostenloses Konto, und erstellen Sie im Dashboard einen Schlüssel vom Typ «Nur Karten», mit Ihrer Website als erlaubtem Referer.
  2. Kopieren Sie den Code unten, und setzen Sie Ihren Schlüssel ein.
  3. Tragen Sie die Position Ihres Geschäfts (so finden Sie sie) und Ihre Zonen ein, und fügen Sie den Code in Ihre Seite ein.

Zonen nach Entfernung

<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@6.13.0/dist/maplibre-gl.css">
<style>
  #map { height: 460px; border-radius: 8px; }
  #answer { margin: 8px 0; font: 16px/1.4 system-ui, sans-serif; }
</style>

<p id="answer">Klicken Sie auf der Karte dorthin, wo Sie Ihre Pizza möchten.</p>
<div id="map"></div>

<script type="module">
import * as maplibregl from "https://unpkg.com/maplibre-gl@6.13.0/dist/maplibre-gl.mjs";

const shop = { name: "Pizzeria Muster", lon: 7.4474, lat: 46.9477 };
// die Zonen von innen nach aussen: bis wie viele km, und was die Lieferung kostet
const zones = [
  { km: 3, label: "Gratis-Lieferung", color: "#2e8b57" },
  { km: 6, label: "Lieferung für CHF 5", color: "#e69f00" }
];

const map = new maplibregl.Map({
  container: "map",
  style: "https://api.overpass.ch/v1/IHR_SCHLÜSSEL/maps/styles/light.json?lang=de",
  center: [shop.lon, shop.lat],
  zoom: 11.3,
  cooperativeGestures: true
});
map.addControl(new maplibregl.NavigationControl({ showCompass: false }));
new maplibregl.Marker({ color: "#c0392b" }).setLngLat([shop.lon, shop.lat]).addTo(map);

map.on("load", () => {
  // unter der Beschriftung, damit Strassen- und Ortsnamen lesbar bleiben
  const labels = map.getStyle().layers.find((l) => l.type === "symbol")?.id;
  for (const z of zones) {
    map.addSource(`zone-${z.km}`, { type: "geojson", data: circle(shop.lon, shop.lat, z.km) });
    map.addLayer({ id: `zone-${z.km}`, type: "fill", source: `zone-${z.km}`,
      paint: { "fill-color": z.color, "fill-opacity": 0.12 } }, labels);
    map.addLayer({ id: `zone-${z.km}-line`, type: "line", source: `zone-${z.km}`,
      paint: { "line-color": z.color, "line-width": 2 } }, labels);
  }
  // die ganze äussere Zone im Blick, auf jedem Bildschirm
  const outer = circle(shop.lon, shop.lat, zones.at(-1).km).geometry.coordinates[0];
  map.fitBounds(outer.reduce((b, p) => b.extend(p), new maplibregl.LngLatBounds()), { padding: 20, duration: 0 });
});

const you = new maplibregl.Marker({ color: "#555" });
map.on("click", (e) => {
  const d = km(shop.lat, shop.lon, e.lngLat.lat, e.lngLat.lng);
  const zone = zones.find((z) => d <= z.km);
  you.setLngLat(e.lngLat).addTo(map);
  document.getElementById("answer").textContent =
    `${zone ? zone.label : "Ausserhalb unseres Liefergebiets"}: ${d.toFixed(1)} km von ${shop.name}.`;
});

// ein Kreis von km um einen Punkt, als GeoJSON-Polygon
function circle(lon, lat, km) {
  const ring = [];
  for (let i = 0; i <= 64; i++) {
    const a = (i / 64) * 2 * Math.PI;
    ring.push([
      lon + (km / (111.32 * Math.cos((lat * Math.PI) / 180))) * Math.sin(a),
      lat + (km / 110.57) * Math.cos(a)
    ]);
  }
  return { type: "Feature", properties: {}, geometry: { type: "Polygon", coordinates: [ring] } };
}

// Entfernung in km zwischen zwei Punkten (Haversine)
function km(lat1, lon1, lat2, lon2) {
  const r = Math.PI / 180;
  const a = Math.sin((lat2 - lat1) * r / 2) ** 2 +
    Math.cos(lat1 * r) * Math.cos(lat2 * r) * Math.sin((lon2 - lon1) * r / 2) ** 2;
  return 12742 * Math.asin(Math.sqrt(a));
}
</script>

zones zählt die Zonen von innen nach aussen auf, jede mit ihrer Entfernung in km, dem Text für die Kundschaft und einer Farbe. Für eine dritte Zone ergänzen Sie eine dritte Zeile. Die Entfernung ist die Luftlinie von Ihrem Geschäft; der Weg auf der Strasse ist länger.

Ein Gebiet, das Sie zeichnen

Liefern Sie nach Quartieren, oder bildet ein Fluss oder eine Autobahn die Grenze, zeichnen Sie das Gebiet selbst:

  1. Öffnen Sie geojson.io, suchen Sie Ihr Gebiet, und zeichnen Sie es mit dem Polygon-Werkzeug. Mehrere getrennte Gebiete sind kein Problem.
  2. Wählen Sie Save › GeoJSON, und legen Sie die Datei als area.geojson neben Ihre Seite.
  3. Verwenden Sie diesen Code statt des Codes oben.
Klicken Sie auf die Karte, um zu sehen, ob wir zu Ihnen kommen.
Die Karte wird geladen…

Der Code für ein Gebiet

<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@6.13.0/dist/maplibre-gl.css">
<style>
  #map { height: 460px; border-radius: 8px; }
  #answer { margin: 8px 0; font: 16px/1.4 system-ui, sans-serif; }
</style>

<p id="answer">Klicken Sie auf die Karte, um zu sehen, ob wir zu Ihnen kommen.</p>
<div id="map"></div>

<script type="module">
import * as maplibregl from "https://unpkg.com/maplibre-gl@6.13.0/dist/maplibre-gl.mjs";

// auf geojson.io mit dem Polygon-Werkzeug gezeichnet und als area.geojson gespeichert
const area = await (await fetch("area.geojson")).json();

const map = new maplibregl.Map({
  container: "map",
  style: "https://api.overpass.ch/v1/IHR_SCHLÜSSEL/maps/styles/light.json?lang=de",
  center: [7.4474, 46.9477],
  zoom: 11,
  cooperativeGestures: true
});
map.addControl(new maplibregl.NavigationControl({ showCompass: false }));

map.on("load", () => {
  const labels = map.getStyle().layers.find((l) => l.type === "symbol")?.id;
  map.addSource("area", { type: "geojson", data: area });
  map.addLayer({ id: "area", type: "fill", source: "area",
    paint: { "fill-color": "#1f5fbf", "fill-opacity": 0.12 } }, labels);
  map.addLayer({ id: "area-line", type: "line", source: "area",
    paint: { "line-color": "#1f5fbf", "line-width": 2 } }, labels);
});

map.on("click", (e) => {
  const p = [e.lngLat.lng, e.lngLat.lat];
  const yes = area.features.some((f) => f.geometry.type === "Polygon" && inside(f.geometry.coordinates[0], p));
  document.getElementById("answer").textContent = yes ? "Ja, wir kommen zu Ihnen." : "Leider liegt das ausserhalb unseres Gebiets.";
});

// liegt der Punkt p im Polygon-Ring? (Strahlverfahren)
function inside(ring, [x, y]) {
  let yes = false;
  for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
    const [xi, yi] = ring[i], [xj, yj] = ring[j];
    if ((yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) yes = !yes;
  }
  return yes;
}
</script>

Die kurze Funktion inside() funktioniert für Gebiete ohne Löcher, wie sie das Polygon-Werkzeug zeichnet. Für Gebiete mit Löchern verwenden Sie booleanPointInPolygon aus der Bibliothek Turf.

Was es kostet

Die erste Ansicht beider Karten lud 6 bis 8 Kartenkacheln, in einem Fenster von 1000 × 640 Pixeln mit MapLibre GL JS 6.13.0.

TarifKartenkacheln im MonatAnsichten mit 8 KachelnFür eine Firmenwebsite
Jedes Konto, kostenlos100'000etwa 12'000✗ Nein, nur für nicht kommerzielle Websites
Maps Small, CHF 9 im Monat2 Millionenetwa 250'000✓ Ja
Maps Medium, CHF 29 im Monat10 Millionenetwa 1'200'000✓ Ja

Preise zuzüglich MWST. Alle Tarife: Maps-Tarife.

Gut zu wissen

  • Die Karte setzt keine Cookies: Weder MapLibre noch unser Kartenserver verwenden welche.
  • Die Quellenangabe © OpenStreetMap muss auf der Karte bleiben. Die Schaltfläche (i) in der Ecke öffnet sie.
  • Um auch Ihre Filialen zu zeigen, kombinieren Sie das mit dem Filialfinder.