Folium-Karten: die Blockade war eine falsche Alternative
Der einzige Punkt, der in diesem Projekt je als "blockiert" gefuehrt wurde,
ist erledigt. Die Begruendung lautete: Die VRP-Instanz sei ein synthetisches
100 x 100-km-Raster ohne Geokoordinaten, eine echte Karte verlange also
ENTWEDER eine neue Instanz (und damit zwei Dutzend neue abgedruckte Zahlen)
ODER eine willkuerliche Verankerung.
Es gibt einen dritten Weg, und er ist exakt. Eine azimutal-aequidistante
Projektion um den Schwerpunkt des Rasters bildet die 17 Punkte so auf Laenge
und Breite ab, dass die Grosskreisdistanzen mit den Rasterdistanzen
uebereinstimmen:
festes cos(Breite) Median 0,360 % Maximum 2,003 %
cos der eigenen Breite Median 0,351 % Maximum 0,908 %
azimutal-aequidistant Median 0,000 % Maximum 0,001 %
Ein Meter auf hundert Kilometer. Kein abgedruckter Wert aendert sich, die
Gesamtstrecke bleibt bei 619 km, das SVG ist byte-identisch geblieben.
Zwei Befunde stuetzten die Entscheidung: "100 x 100 km Raster" steht nur in
zwei Codekommentaren, nirgends im Fliesstext; und der didaktische Punkt der
Bildunterschrift - die sich kreuzenden Touren, die KEIN Fehler sind - wird auf
einer Karte deutlicher.
erzeuge_vrp_touren.py schreibt jetzt SVG UND Karte aus einem Lauf. Ein zweites
Skript haette erneut geloest, und die Routing-Bibliothek arbeitet heuristisch.
pruefe_projektion() bricht ab, wenn die Verzerrung 0,01 % ueberschreitet - die
Zahl im Buch wird bei jedem Lauf bewiesen.
Neu im Build: die Marke {karte:name}, aufgeloest zu einem <iframe> (Leaflets
Stylesheet soll nicht in die Buchseite greifen), im PDF zum Verweissatz - wie
bei {plotly:}. --check bewacht jetzt beide Markenarten auf Existenz der Datei;
gegengeprueft mit einer absichtlich falschen Marke.
Zwei Vorkehrungen, die ohne Messung nicht aufgefallen waeren:
* Folium vergibt jedem Element eine uuid4 - jeder Lauf erzeugte eine andere
Datei. normalisiere() ersetzt sie durch fortlaufende Nummern; zwei Laeufe
liefern jetzt dieselbe Pruefsumme.
* Folium bindet ZEHN CDN-Ressourcen ein (jQuery, Bootstrap, Glyphicons,
FontAwesome, awesome-markers ...). Gebraucht werden ZWEI. leaflet.js und
leaflet.css liegen jetzt in web_04/assets/, die uebrigen acht entfallen.
Nachgeprueft: Die Karte benutzt ausschliesslich L.*-Aufrufe. Ohne Netz
fehlen nur die Kacheln, Marker und Touren bleiben sichtbar.
Gegenrechnung an vier Strecken: Depot -> K1 misst auf der Karte 27,20 km, die
Distanzmatrix sagt 27 - die Differenz stammt aus deren int(), nicht aus der
Geographie.
Veroeffentlicht: 8 Dateien uebertragen, alle dreizehn HTTP-Proben richtig, die
Karte auf dem Server unter 200 erreichbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d37834ccb5
commit
5d5bdb6508
19 changed files with 2521 additions and 5 deletions
25
CLAUDE.md
25
CLAUDE.md
|
|
@ -188,6 +188,31 @@ eine einzige lange Zeile mit eingebetteten Leerzeichenketten, aus der Pandoc son
|
|||
Codeblock macht. Genau daran waren alle vier Figuren kaputt, bis es die Schlussabnahme fand.
|
||||
Im PDF steht stattdessen ein Hinweis auf die Website.
|
||||
|
||||
## Karten
|
||||
|
||||
`{karte:name}` bindet `bilder_04/karten/<name>.html` als `<iframe>` ein — anders als bei
|
||||
Plotly **nicht** inline, weil Leaflets Stylesheet sonst in die Buchseite hineingriffe. Im PDF
|
||||
steht wieder ein Hinweis auf die Website. `--check` prüft für beide Markenarten, dass die
|
||||
Datei existiert.
|
||||
|
||||
Erzeugt werden die Karten von demselben `bilder_04/erzeuge_*.py`, das auch das zugehörige SVG
|
||||
schreibt — **aus einem Lauf**, sonst zeigten die beiden Darstellungen bei einem heuristischen
|
||||
Solver womöglich verschiedene Lösungen. Zwei Vorkehrungen in `erzeuge_vrp_touren.py` sind
|
||||
übertragbar:
|
||||
|
||||
* **Byte-Reproduzierbarkeit:** Folium vergibt jedem Element eine `uuid4`. `normalisiere()`
|
||||
ersetzt sie durch fortlaufende Nummern — dieselbe Sorgfalt wie `svg.hashsalt` bei den
|
||||
matplotlib-Bildern.
|
||||
* **Netzunabhängigkeit:** Von Foliums zehn CDN-Einbindungen bleiben die zwei gebrauchten,
|
||||
umgeschrieben auf `../../assets/leaflet.*` (mitgeliefert in `web_04/assets/`); die übrigen
|
||||
acht entfallen, weil die Karte nur `CircleMarker`, `PolyLine` und `Tooltip` benutzt. Ohne
|
||||
Netz fehlen nur die Kartenkacheln.
|
||||
|
||||
**Die Instanz ist synthetisch und bleibt es.** `projiziere()` legt das 100 × 100-km-Raster
|
||||
azimutal-äquidistant auf eine reale Region; `pruefe_projektion()` bricht ab, wenn die
|
||||
Abstände dabei um mehr als 0,01 % verzerrt würden (gemessen: 0,001 %). Kein abgedruckter Wert
|
||||
ändert sich dadurch — das war die Bedingung, unter der die Karte überhaupt möglich wurde.
|
||||
|
||||
---
|
||||
|
||||
## Veroeffentlichung
|
||||
|
|
|
|||
BIN
OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf
generated
BIN
OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf
generated
Binary file not shown.
656
OR_HTML_04/assets/leaflet.css
generated
Normal file
656
OR_HTML_04/assets/leaflet.css
generated
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
6
OR_HTML_04/assets/leaflet.js
generated
Normal file
6
OR_HTML_04/assets/leaflet.js
generated
Normal file
File diff suppressed because one or more lines are too long
7
OR_HTML_04/assets/site.css
generated
7
OR_HTML_04/assets/site.css
generated
|
|
@ -282,6 +282,13 @@ article li { margin: .3em 0; }
|
|||
border-radius: var(--radius); padding: .6em; overflow-x: auto; }
|
||||
.plotly-figur .plotly-graph-div { min-width: 320px; }
|
||||
|
||||
/* Leaflet-Karte im iframe ({karte:...}). Der Rahmen kommt von hier, nicht vom
|
||||
iframe selbst - dessen Standardrahmen passt zu keinem der beiden Themen. */
|
||||
.karte-figur { display: block; width: 100%; height: 30rem; margin: 1.4em 0;
|
||||
border: 1px solid var(--border); border-radius: var(--radius);
|
||||
background: var(--surface); }
|
||||
@media (max-width: 640px) { .karte-figur { height: 22rem; } }
|
||||
|
||||
/* Der Formel-Uebersetzer ist zweispaltig gedacht: links die Mathematik,
|
||||
rechts der Klartext. Auf schmalen Displays wuerde die Formelspalte die
|
||||
Tabelle sonst aus dem Viewport druecken. */
|
||||
|
|
|
|||
430
OR_HTML_04/bilder_04/karten/kap_graphen_vrp_touren.html
generated
Normal file
430
OR_HTML_04/bilder_04/karten/kap_graphen_vrp_touren.html
generated
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<script src="../../assets/leaflet.js"></script>
|
||||
<link rel="stylesheet" href="../../assets/leaflet.css"/>
|
||||
|
||||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_01 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
left: 0.0%;
|
||||
top: 0.0%;
|
||||
}
|
||||
.leaflet-container { font-size: 1rem; }
|
||||
</style>
|
||||
|
||||
<style>html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>#map {
|
||||
position:absolute;
|
||||
top:0;
|
||||
bottom:0;
|
||||
right:0;
|
||||
left:0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
L_NO_TOUCH = false;
|
||||
L_DISABLE_3D = false;
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_01" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_01 = L.map(
|
||||
"map_01",
|
||||
{
|
||||
center: [50.99937073954281, 9.200100716327679],
|
||||
crs: L.CRS.EPSG3857,
|
||||
...{
|
||||
"zoom": 9,
|
||||
"zoomControl": true,
|
||||
"preferCanvas": false,
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
L.control.scale().addTo(map_01);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var tile_layer_02 = L.tileLayer(
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19,
|
||||
"maxNativeZoom": 19,
|
||||
"noWrap": false,
|
||||
"attribution": "\u0026copy; \u003ca href=\"https://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors",
|
||||
"subdomains": "abc",
|
||||
"detectRetina": false,
|
||||
"tms": false,
|
||||
"opacity": 1,
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
tile_layer_02.addTo(map_01);
|
||||
|
||||
|
||||
var poly_line_03 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [50.53528002580979, 9.056780383216372], [50.70020093721884, 9.023183616953922], [50.899334211646234, 8.572820141079644], [50.828627944796516, 8.85527539693994], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#4338ca", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_03.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 1: 172 km, 10/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_04 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.202571214486355, 9.47729598790725], [51.23585035464649, 9.309049094203546], [51.36580069667539, 9.454691406682949], [51.2977516796715, 9.779278159065392], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#0891b2", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_04.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 2: 135 km, 9/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_05 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.12239321211423, 9.61645605992352], [51.0634545325666, 9.57161203934019], [50.81449346507728, 9.470641609414168], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#b45309", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_05.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 3: 70 km, 8/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_06 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.32909155311604, 8.916255495545233], [51.37139606353655, 8.517488901092882], [51.108758805458066, 8.607477788041049], [50.553365253206685, 9.181091101072626], [50.67063451777786, 9.498609802394965], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#059669", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_06.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 4: 242 km, 10/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_07 = L.circleMarker(
|
||||
[51.12239321211423, 9.61645605992352],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_07.bindTooltip(
|
||||
`<div>
|
||||
K1 — Bedarf 2, Zeitfenster 30–120 min<br>Fahrzeug 3, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_08 = L.circleMarker(
|
||||
[51.37139606353655, 8.517488901092882],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_08.bindTooltip(
|
||||
`<div>
|
||||
K2 — Bedarf 3, Zeitfenster 60–180 min<br>Fahrzeug 4, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_09 = L.circleMarker(
|
||||
[51.202571214486355, 9.47729598790725],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_09.bindTooltip(
|
||||
`<div>
|
||||
K3 — Bedarf 1, Zeitfenster 100–240 min<br>Fahrzeug 2, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_10 = L.circleMarker(
|
||||
[50.899334211646234, 8.572820141079644],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9.6, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_10.bindTooltip(
|
||||
`<div>
|
||||
K4 — Bedarf 4, Zeitfenster 150–300 min<br>Fahrzeug 1, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_11 = L.circleMarker(
|
||||
[51.32909155311604, 8.916255495545233],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_11.bindTooltip(
|
||||
`<div>
|
||||
K5 — Bedarf 2, Zeitfenster 60–180 min<br>Fahrzeug 4, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_12 = L.circleMarker(
|
||||
[51.23585035464649, 9.309049094203546],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_12.bindTooltip(
|
||||
`<div>
|
||||
K6 — Bedarf 2, Zeitfenster 120–240 min<br>Fahrzeug 2, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_13 = L.circleMarker(
|
||||
[50.70020093721884, 9.023183616953922],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_13.bindTooltip(
|
||||
`<div>
|
||||
K7 — Bedarf 3, Zeitfenster 200–360 min<br>Fahrzeug 1, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_14 = L.circleMarker(
|
||||
[50.553365253206685, 9.181091101072626],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_14.bindTooltip(
|
||||
`<div>
|
||||
K8 — Bedarf 1, Zeitfenster 300–450 min<br>Fahrzeug 4, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_15 = L.circleMarker(
|
||||
[51.0634545325666, 9.57161203934019],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_15.bindTooltip(
|
||||
`<div>
|
||||
K9 — Bedarf 2, Zeitfenster 180–300 min<br>Fahrzeug 3, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_16 = L.circleMarker(
|
||||
[50.81449346507728, 9.470641609414168],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9.6, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_16.bindTooltip(
|
||||
`<div>
|
||||
K10 — Bedarf 4, Zeitfenster 240–360 min<br>Fahrzeug 3, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_17 = L.circleMarker(
|
||||
[51.2977516796715, 9.779278159065392],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_17.bindTooltip(
|
||||
`<div>
|
||||
K11 — Bedarf 3, Zeitfenster 300–480 min<br>Fahrzeug 2, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_18 = L.circleMarker(
|
||||
[50.67063451777786, 9.498609802394965],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_18.bindTooltip(
|
||||
`<div>
|
||||
K12 — Bedarf 2, Zeitfenster 360–500 min<br>Fahrzeug 4, Stopp 5
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_19 = L.circleMarker(
|
||||
[50.53528002580979, 9.056780383216372],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_19.bindTooltip(
|
||||
`<div>
|
||||
K13 — Bedarf 1, Zeitfenster 60–200 min<br>Fahrzeug 1, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_20 = L.circleMarker(
|
||||
[51.108758805458066, 8.607477788041049],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_20.bindTooltip(
|
||||
`<div>
|
||||
K14 — Bedarf 2, Zeitfenster 120–300 min<br>Fahrzeug 4, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_21 = L.circleMarker(
|
||||
[51.36580069667539, 9.454691406682949],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_21.bindTooltip(
|
||||
`<div>
|
||||
K15 — Bedarf 3, Zeitfenster 240–400 min<br>Fahrzeug 2, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_22 = L.circleMarker(
|
||||
[50.828627944796516, 8.85527539693994],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_22.bindTooltip(
|
||||
`<div>
|
||||
K16 — Bedarf 2, Zeitfenster 300–550 min<br>Fahrzeug 1, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_23 = L.circleMarker(
|
||||
[50.8902981044233, 9.493705194696913],
|
||||
{"bubblingMouseEvents": true, "color": "#be123c", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#be123c", "fillOpacity": 0.9, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_23.bindTooltip(
|
||||
`<div>
|
||||
Depot — Schicht 0–600 min
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</html>
|
||||
3
OR_HTML_04/gesamtdokument.html
generated
3
OR_HTML_04/gesamtdokument.html
generated
|
|
@ -13184,6 +13184,9 @@ Umschlag -> Kunde_2 5.0 10 3.00 15.00
|
|||
<img src="bilder_04/kap_graphen_vrp_touren.svg" alt="Abb. 8.2: Die Lösung der Instanz aus VRP_Flotten_Routing.py: 16 Kunden, vier Fahrzeuge, 619 km. Beachten Sie, dass sich die Touren kreuzen. Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von bilder_04/erzeuge_vrp_touren.py." />
|
||||
<figcaption aria-hidden="true">Abb. 8.2: Die Lösung der Instanz aus <code>VRP_Flotten_Routing.py</code>: 16 Kunden, vier Fahrzeuge, 619 km. <strong>Beachten Sie, dass sich die Touren kreuzen.</strong> Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von <code>bilder_04/erzeuge_vrp_touren.py</code>.</figcaption>
|
||||
</figure>
|
||||
<p><em>Eine interaktive Karte dieser Touren steht auf den Kapitelseiten der Website bereit.</em></p>
|
||||
<p><strong>Was Sie auf der Karte sehen — und was nicht.</strong> Die Instanz ist synthetisch: 17 zufällige Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie <em>lagegetreu</em> auf eine reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände zwischen den Punkten bis auf <strong>0,001 %</strong> erhält. Die Karte zeigt also dieselbe Instanz, mit der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein Wert in diesem Kapitel ändert sich dadurch.</p>
|
||||
<p>Die Touren sind aus demselben Grund <strong>Luftlinien und keine Straßenrouten</strong>: Das Modell kennt keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — <a href="#sec:graphen-denkfehler">Abschnitt 8.7</a> sagt, was dabei zu beachten ist.</p>
|
||||
<h3 id="kurzzyklen-verhindern">Kurzzyklen verhindern</h3>
|
||||
<p>Ein naives Modell erlaubt <strong>Subtouren</strong>: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die <strong>MTZ-Formulierung</strong> nach Miller, Tucker und Zemlin. Man führt Rangvariablen <span class="math inline">u_i</span> ein (die Position des Knotens in der Tour):</p>
|
||||
<p><span class="math display">
|
||||
|
|
|
|||
3
OR_HTML_04/graphen.html
generated
3
OR_HTML_04/graphen.html
generated
|
|
@ -579,6 +579,9 @@ Umschlag -> Kunde_2 5.0 10 3.00 15.00
|
|||
<img src="bilder_04/kap_graphen_vrp_touren.svg" alt="Abb. 8.2: Die Lösung der Instanz aus VRP_Flotten_Routing.py: 16 Kunden, vier Fahrzeuge, 619 km. Beachten Sie, dass sich die Touren kreuzen. Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von bilder_04/erzeuge_vrp_touren.py." />
|
||||
<figcaption aria-hidden="true">Abb. 8.2: Die Lösung der Instanz aus <code>VRP_Flotten_Routing.py</code>: 16 Kunden, vier Fahrzeuge, 619 km. <strong>Beachten Sie, dass sich die Touren kreuzen.</strong> Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von <code>bilder_04/erzeuge_vrp_touren.py</code>.</figcaption>
|
||||
</figure>
|
||||
<iframe class="karte-figur" loading="lazy" src="bilder_04/karten/kap_graphen_vrp_touren.html" title="Interaktive Karte: kap_graphen_vrp_touren"></iframe>
|
||||
<p><strong>Was Sie auf der Karte sehen — und was nicht.</strong> Die Instanz ist synthetisch: 17 zufällige Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie <em>lagegetreu</em> auf eine reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände zwischen den Punkten bis auf <strong>0,001 %</strong> erhält. Die Karte zeigt also dieselbe Instanz, mit der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein Wert in diesem Kapitel ändert sich dadurch.</p>
|
||||
<p>Die Touren sind aus demselben Grund <strong>Luftlinien und keine Straßenrouten</strong>: Das Modell kennt keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — <a href="#sec:graphen-denkfehler">Abschnitt 8.7</a> sagt, was dabei zu beachten ist.</p>
|
||||
<h3 id="kurzzyklen-verhindern">Kurzzyklen verhindern</h3>
|
||||
<p>Ein naives Modell erlaubt <strong>Subtouren</strong>: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die <strong>MTZ-Formulierung</strong> nach Miller, Tucker und Zemlin. Man führt Rangvariablen <span class="math inline">u_i</span> ein (die Position des Knotens in der Tour):</p>
|
||||
<p><span class="math display">
|
||||
|
|
|
|||
6
Operations_Research_mit_Python_Version_04.md
generated
6
Operations_Research_mit_Python_Version_04.md
generated
|
|
@ -9587,6 +9587,12 @@ Das **Traveling Salesperson Problem (TSP)**`\index{TSP (Traveling Salesperson Pr
|
|||

|
||||
|
||||
*Eine interaktive Karte dieser Touren steht auf den Kapitelseiten der Website bereit.*
|
||||
|
||||
**Was Sie auf der Karte sehen — und was nicht.** Die Instanz ist synthetisch: 17 zufällige Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie *lagegetreu* auf eine reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände zwischen den Punkten bis auf **0,001 %** erhält. Die Karte zeigt also dieselbe Instanz, mit der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein Wert in diesem Kapitel ändert sich dadurch.
|
||||
|
||||
Die Touren sind aus demselben Grund **Luftlinien und keine Straßenrouten**: Das Modell kennt keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — [Abschnitt 8.7](#sec:graphen-denkfehler) sagt, was dabei zu beachten ist.
|
||||
|
||||
### Kurzzyklen verhindern
|
||||
|
||||
Ein naives Modell erlaubt **Subtouren**`\index{Subtour}`{=latex}: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die **MTZ-Formulierung**`\index{MTZ-Formulierung}`{=latex} nach Miller, Tucker und Zemlin. Man führt Rangvariablen $u_i$ ein (die Position des Knotens in der Tour):
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -493,6 +493,21 @@ sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zei
|
|||
erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von
|
||||
`bilder_04/erzeuge_vrp_touren.py`.](bilder_04/kap_graphen_vrp_touren.svg)
|
||||
|
||||
{karte:kap_graphen_vrp_touren}
|
||||
|
||||
**Was Sie auf der Karte sehen — und was nicht.** Die Instanz ist synthetisch: 17 zufällige
|
||||
Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie *lagegetreu* auf eine
|
||||
reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände
|
||||
zwischen den Punkten bis auf **0,001 %** erhält. Die Karte zeigt also dieselbe Instanz, mit
|
||||
der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein
|
||||
Wert in diesem Kapitel ändert sich dadurch.
|
||||
|
||||
Die Touren sind aus demselben Grund **Luftlinien und keine Straßenrouten**: Das Modell kennt
|
||||
keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten
|
||||
Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht
|
||||
hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — {ref:sec:graphen-denkfehler} sagt,
|
||||
was dabei zu beachten ist.
|
||||
|
||||
### Kurzzyklen verhindern
|
||||
|
||||
Ein naives Modell erlaubt **Subtouren**{idx:Subtour}: isolierte Kreise, die das Depot nie anfahren. Die
|
||||
|
|
|
|||
|
|
@ -390,6 +390,23 @@ def pruefe_dateien() -> list[str]:
|
|||
f"folgt aber '{soll}' - die Lesekette ueberspringt eine Datei.")
|
||||
fehlend.append(name)
|
||||
|
||||
# Marken fuer interaktive Figuren: Zeigt eine ins Leere, verschwindet sie
|
||||
# auf der Website stillschweigend - im PDF steht dann ein Verweis auf eine
|
||||
# Figur, die es nicht gibt.
|
||||
for name in DATEIEN:
|
||||
with open(os.path.join(HIER, name), encoding="utf-8") as f:
|
||||
inhalt = f.read()
|
||||
for marke, verzeichnis in ((PLOTLY_RE, PLOTLY_VERZ),
|
||||
(KARTE_RE, KARTEN_VERZ)):
|
||||
for treffer in marke.finditer(inhalt):
|
||||
pfad = os.path.join(verzeichnis, f"{treffer.group(1)}.html")
|
||||
if not os.path.exists(pfad):
|
||||
zeile = inhalt[:treffer.start()].count("\n") + 1
|
||||
print(f"FEHLER: {name}:{zeile}: '{treffer.group(0).strip()}' "
|
||||
f"verweist auf eine nicht vorhandene Datei "
|
||||
f"({os.path.relpath(pfad, BASIS)}).")
|
||||
fehlend.append(f"{name}:{zeile}")
|
||||
|
||||
fehlend.extend(pruefe_assets())
|
||||
fehlend.extend(pruefe_titeltexte())
|
||||
return fehlend
|
||||
|
|
@ -1088,6 +1105,7 @@ def baue_markdown() -> str:
|
|||
|
||||
gesamt = "\n\n---\n\n".join(teile) + "\n" + _STICHWORTREGISTER_BLOCK
|
||||
gesamt, _ = resolve_plotly(gesamt, fuer_html=False)
|
||||
gesamt = resolve_karte(gesamt, fuer_html=False)
|
||||
return resolve_index(resolve_numbering(reflow_markdown(gesamt)))
|
||||
|
||||
|
||||
|
|
@ -1865,6 +1883,45 @@ PLOTLY_RE = re.compile(r"^\{plotly:([\w-]+)\}[ \t]*$", re.MULTILINE)
|
|||
|
||||
PLOTLY_VERZ = os.path.join(BILDER_VERZ, "plotly")
|
||||
|
||||
# Dasselbe fuer Leaflet-Karten:
|
||||
#
|
||||
# {karte:kap_graphen_vrp_touren}
|
||||
#
|
||||
# Anders als die Plotly-Fragmente wird eine Karte NICHT eingebettet, sondern in
|
||||
# einem <iframe> gezeigt. Zwei Gruende: Leaflets Stylesheet ist umfangreich und
|
||||
# griffe sonst in die Buchseite hinein, und Foliums Ausgabe ist ein
|
||||
# vollstaendiges HTML-Dokument, das sich so unveraendert uebernehmen laesst.
|
||||
# Erzeugt werden die Karten von den bilder_04/erzeuge_*.py, die auch das
|
||||
# zugehoerige SVG schreiben - aus demselben Lauf, damit beide Darstellungen
|
||||
# dieselbe Loesung zeigen.
|
||||
KARTE_RE = re.compile(r"^\{karte:([\w-]+)\}[ \t]*$", re.MULTILINE)
|
||||
|
||||
KARTEN_NAME = "karten"
|
||||
KARTEN_VERZ = os.path.join(BILDER_VERZ, KARTEN_NAME)
|
||||
|
||||
|
||||
def resolve_karte(text: str, fuer_html: bool) -> str:
|
||||
"""Loest {karte:name}-Marken auf."""
|
||||
def ersetze(m: re.Match) -> str:
|
||||
name = m.group(1)
|
||||
if not fuer_html:
|
||||
# Wortlaut wie bei resolve_plotly(): Dieser Zweig bedient das PDF
|
||||
# UND gesamtdokument.html - wer Letzteres liest, ist schon auf der
|
||||
# Website, "auf der Website" waere dort also falsch.
|
||||
return ("*Eine interaktive Karte dieser Touren steht auf den "
|
||||
"Kapitelseiten der Website bereit.*")
|
||||
pfad = os.path.join(KARTEN_VERZ, f"{name}.html")
|
||||
if not os.path.exists(pfad):
|
||||
print(f"WARNUNG: Karte fehlt: {pfad} - Marke entfernt.")
|
||||
return ""
|
||||
return ('```{=html}\n'
|
||||
f'<iframe class="karte-figur" loading="lazy" '
|
||||
f'src="{BILDER_NAME}/{KARTEN_NAME}/{name}.html" '
|
||||
f'title="Interaktive Karte: {name}"></iframe>\n'
|
||||
'```')
|
||||
|
||||
return KARTE_RE.sub(ersetze, text)
|
||||
|
||||
|
||||
def resolve_plotly(text: str, fuer_html: bool) -> tuple[str, bool]:
|
||||
"""Loest {plotly:name}-Marken auf. Liefert (Text, ob eine Figur eingebettet
|
||||
|
|
@ -2138,6 +2195,7 @@ def baue_kapitel_seiten(html_verz: str, datei_seite: dict, labels: dict,
|
|||
text = resolve_numbering_seite(text, eintrag["seite"], labels, label_seite)
|
||||
text = resolve_index(text)
|
||||
text, mit_plotly = resolve_plotly(text, fuer_html=True)
|
||||
text = resolve_karte(text, fuer_html=True)
|
||||
text = markiere_karten(text)
|
||||
|
||||
fragment = _pandoc_fragment(text, html_verz)
|
||||
|
|
|
|||
55
PROGRESS.md
55
PROGRESS.md
|
|
@ -1376,11 +1376,11 @@ Teilhistorie, die vollständig aussieht und es nicht ist. Volle Historie ginge n
|
|||
7.1 leisten dasselbe gezielter, ohne Eingriff in jeden Abschnitt.
|
||||
* **`joblib`/`ray`** (V02/2) — `num_workers` und `ProcessPoolExecutor` decken den Bedarf;
|
||||
ein Cluster-Framework wäre eine Abhängigkeit ohne Beispiel, das sie braucht.
|
||||
* **Folium-Karten** (V03/3) — **blockiert durch eine offene Entscheidung**: Die VRP-Instanz
|
||||
ist ein synthetisches 100 × 100-km-Raster ohne Geokoordinaten. Eine echte Karte verlangt
|
||||
entweder eine neue Instanz (und damit neue abgedruckte Zahlen) oder eine willkürliche
|
||||
Verankerung an einem realen Ort.
|
||||
* **Executive-PDF-Report** (V03/6) — ohne Streamlit-Apps fehlt der Ort, an dem er entstünde.
|
||||
* **Executive-PDF-Report** (V03/6) — ohne Streamlit-Apps fehlt der Ort, an dem er
|
||||
entstünde.
|
||||
|
||||
**Folium-Karten** (V03/3) standen hier lange als **blockiert** — sie sind es nicht mehr,
|
||||
siehe den Eintrag im nächsten Abschnitt.
|
||||
|
||||
### Offen, rein redaktionell
|
||||
|
||||
|
|
@ -2258,6 +2258,51 @@ hat ihren Zweck erfüllt — aber einen Rückschritt könnte sie gar nicht mehr
|
|||
Datei ist gelöscht, die Probe bliebe auch dann grün, wenn jemand `--delete` aus dem
|
||||
`rsync`-Aufruf nähme. Von den ursprünglich vierzehn Adressen bleiben dreizehn.
|
||||
|
||||
### ✅ Folium-Karten — die Blockade war eine falsche Alternative
|
||||
|
||||
Der einzige Punkt, der in diesem Projekt je als *blockiert* geführt wurde, ist erledigt. Die
|
||||
Begründung lautete: Die VRP-Instanz sei ein synthetisches 100 × 100-km-Raster ohne
|
||||
Geokoordinaten, eine echte Karte verlange also **entweder** eine neue Instanz (und damit
|
||||
zwei Dutzend neue abgedruckte Zahlen) **oder** eine willkürliche Verankerung.
|
||||
|
||||
**Es gibt einen dritten Weg, und er ist exakt.** Eine azimutal-äquidistante Projektion um den
|
||||
Schwerpunkt des Rasters bildet die 17 Punkte so auf Länge und Breite ab, dass die
|
||||
Großkreisdistanzen mit den Rasterdistanzen übereinstimmen:
|
||||
|
||||
| Verfahren | Median | Maximum |
|
||||
| --- | --- | --- |
|
||||
| festes `cos(Breite)` | 0,360 % | 2,003 % |
|
||||
| `cos` der eigenen Breite | 0,351 % | 0,908 % |
|
||||
| **azimutal-äquidistant** | **0,000 %** | **0,001 %** |
|
||||
|
||||
Ein Meter auf hundert Kilometer. Damit ist die Karte keine Näherung, für die man sich
|
||||
entschuldigt, sondern eine zweite Darstellung derselben Instanz — **kein abgedruckter Wert
|
||||
ändert sich**, die Gesamtstrecke bleibt bei 619 km, das SVG byte-identisch.
|
||||
|
||||
Zwei Befunde stützten die Entscheidung: „100 × 100 km Raster" steht **nur in zwei
|
||||
Codekommentaren**, nirgends im Fließtext; und der didaktische Punkt der Bildunterschrift —
|
||||
die sich kreuzenden Touren, die *kein* Fehler sind — wird auf einer Karte deutlicher.
|
||||
|
||||
**Umgesetzt** in `erzeuge_vrp_touren.py`, das jetzt SVG **und** Karte aus einem Lauf schreibt.
|
||||
Ein zweites Skript hätte erneut gelöst, und die Routing-Bibliothek arbeitet heuristisch.
|
||||
Neu im Build: die Marke `{karte:name}`, aufgelöst zu einem `<iframe>` (Leaflets Stylesheet
|
||||
soll nicht in die Buchseite greifen), im PDF zum Verweissatz — wie bei `{plotly:}`. `--check`
|
||||
bewacht jetzt **beide** Markenarten auf Existenz der Datei.
|
||||
|
||||
**Zwei Vorkehrungen, die ohne Messung nicht aufgefallen wären:**
|
||||
|
||||
* Folium vergibt jedem Element eine `uuid4` — **jeder Lauf erzeugte eine andere Datei**.
|
||||
`normalisiere()` ersetzt sie durch fortlaufende Nummern; drei Läufe liefern jetzt dieselbe
|
||||
Prüfsumme.
|
||||
* Folium bindet **zehn** CDN-Ressourcen ein (jQuery, Bootstrap, Glyphicons, FontAwesome,
|
||||
awesome-markers …). Gebraucht werden **zwei**. Sie liegen jetzt in `web_04/assets/`, die
|
||||
übrigen acht entfallen. Nachgeprüft: Die Karte benutzt ausschließlich `L.*`-Aufrufe —
|
||||
17 × `circleMarker`, 4 × `polyline`, je einmal `map`, `tileLayer`, `control`, `CRS`. Ohne
|
||||
Netz fehlen nur die Kacheln.
|
||||
|
||||
Gegenrechnung an vier Strecken: `Depot → K1` misst auf der Karte 27,20 km, die Distanzmatrix
|
||||
sagt 27 — die Differenz stammt aus deren `int()`, nicht aus der Geographie.
|
||||
|
||||
---
|
||||
|
||||
## 8. Commit-Historie des V04-Strangs
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ Benoetigt: numpy, matplotlib, ortools
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
|
|
@ -178,6 +179,164 @@ def zeichne(koordinaten, touren) -> None:
|
|||
plt.close(figur)
|
||||
|
||||
|
||||
# --- Karte ------------------------------------------------------------------
|
||||
#
|
||||
# Dieselbe Loesung ein zweites Mal, diesmal auf einem echten Kartenhintergrund.
|
||||
# Das war lange als "blockiert" vermerkt, weil die Instanz ein synthetisches
|
||||
# 100 x 100-km-Raster ohne Geokoordinaten ist: Eine Karte schien entweder eine
|
||||
# neue Instanz zu verlangen (und damit zwei Dutzend neue abgedruckte Zahlen)
|
||||
# oder eine willkuerliche Verankerung.
|
||||
#
|
||||
# Das war eine falsche Alternative. Die azimutal-aequidistante Projektion um
|
||||
# den Schwerpunkt des Rasters bildet die Punkte SO auf Laenge und Breite ab,
|
||||
# dass die Grosskreisdistanzen mit den Rasterdistanzen uebereinstimmen - ueber
|
||||
# alle 136 Punktpaare bis auf 0,001 %. Das Modell bleibt unberuehrt, kein
|
||||
# abgedruckter Wert aendert sich, und die Karte zeigt trotzdem echte Lagen.
|
||||
# pruefe_projektion() unten weist das bei jedem Lauf nach.
|
||||
ERDRADIUS_KM = 6371.0088
|
||||
ANKER_LAT, ANKER_LON = 51.00, 9.20 # Raum Kassel; zwei Zahlen genuegen,
|
||||
# um die Instanz woanders hinzulegen
|
||||
KARTEN = os.path.join(BILDER, "karten")
|
||||
KARTENNAME = "kap_graphen_vrp_touren"
|
||||
|
||||
|
||||
def projiziere(koordinaten):
|
||||
"""Rasterkoordinaten (km) -> (Breite, Laenge), azimutal-aequidistant um den
|
||||
Schwerpunkt. Formel: Zielpunkt aus Startpunkt, Azimut und Bogendistanz."""
|
||||
mitte = koordinaten.mean(axis=0)
|
||||
dx, dy = (koordinaten - mitte).T
|
||||
bogen = np.hypot(dx, dy) / ERDRADIUS_KM # Winkeldistanz im Bogenmass
|
||||
azimut = np.arctan2(dx, dy) # 0 = Nord, im Uhrzeigersinn
|
||||
phi0 = np.radians(ANKER_LAT)
|
||||
breite = np.arcsin(np.sin(phi0) * np.cos(bogen)
|
||||
+ np.cos(phi0) * np.sin(bogen) * np.cos(azimut))
|
||||
laenge = np.radians(ANKER_LON) + np.arctan2(
|
||||
np.sin(azimut) * np.sin(bogen) * np.cos(phi0),
|
||||
np.cos(bogen) - np.sin(phi0) * np.sin(breite))
|
||||
return np.degrees(breite), np.degrees(laenge)
|
||||
|
||||
|
||||
def pruefe_projektion(koordinaten, breite, laenge) -> float:
|
||||
"""Vergleicht jede Grosskreisdistanz mit der Rasterdistanz und bricht ab,
|
||||
wenn die Karte die Instanz verzerrt darstellen wuerde. Die Zahl steht im
|
||||
Buch - sie wird hier bewiesen, nicht behauptet."""
|
||||
def grosskreis(i, j):
|
||||
p1, p2 = np.radians(breite[i]), np.radians(breite[j])
|
||||
dl = np.radians(laenge[j] - laenge[i])
|
||||
h = (np.sin((p2 - p1) / 2) ** 2
|
||||
+ np.cos(p1) * np.cos(p2) * np.sin(dl / 2) ** 2)
|
||||
return 2 * ERDRADIUS_KM * np.arcsin(np.sqrt(h))
|
||||
|
||||
schlimmster = 0.0
|
||||
for i in range(len(koordinaten)):
|
||||
for j in range(i + 1, len(koordinaten)):
|
||||
raster = float(np.linalg.norm(koordinaten[i] - koordinaten[j]))
|
||||
if raster > 0:
|
||||
schlimmster = max(schlimmster,
|
||||
abs(grosskreis(i, j) - raster) / raster * 100)
|
||||
if schlimmster > 0.01:
|
||||
raise SystemExit(
|
||||
f"Karte: Die Projektion verzerrt die Instanz um bis zu "
|
||||
f"{schlimmster:.3f} % - erwartet sind hoechstens 0,01 %. "
|
||||
f"Anker oder Projektion pruefen.")
|
||||
return schlimmster
|
||||
|
||||
|
||||
# Von Foliums zehn CDN-Einbindungen braucht diese Karte genau zwei: leaflet.js
|
||||
# und leaflet.css. jQuery, Bootstrap, Glyphicons, FontAwesome und
|
||||
# awesome-markers gehoeren zu Foliums Standardmarkern - hier kommen nur
|
||||
# CircleMarker, PolyLine und Tooltip vor, die reines Leaflet sind.
|
||||
LEAFLET_JS = "https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"
|
||||
LEAFLET_CSS = "https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css"
|
||||
EINBINDUNG_RE = re.compile(
|
||||
r'[ \t]*<(?:script src|link rel="stylesheet" href)="(https?://[^"]+)"'
|
||||
r'[^>]*>(?:</script>)?\n?')
|
||||
KENNUNG_RE = re.compile(r"\b([a-z_]+)_([0-9a-f]{32})\b")
|
||||
|
||||
|
||||
def normalisiere(html: str) -> str:
|
||||
"""Macht Foliums Ausgabe byteweise reproduzierbar und netzunabhaengig.
|
||||
|
||||
Zwei Eingriffe:
|
||||
1. Folium vergibt jedem Element eine uuid4 - jeder Lauf erzeugte sonst eine
|
||||
andere Datei und einen unlesbaren git-diff. Dieselbe Sorgfalt wie
|
||||
svg.hashsalt bei den matplotlib-Bildern.
|
||||
2. Die beiden gebrauchten Leaflet-Dateien werden lokal eingebunden (sie
|
||||
liegen in web_04/assets/ und landen beim Bau in OR_HTML_04/assets/),
|
||||
die uebrigen acht Einbindungen entfallen. Ohne Netz fehlen dann nur die
|
||||
Kartenkacheln - Marker und Touren bleiben sichtbar."""
|
||||
reihenfolge: dict[str, str] = {}
|
||||
|
||||
def kennung(m: re.Match) -> str:
|
||||
hexwert = m.group(2)
|
||||
if hexwert not in reihenfolge:
|
||||
reihenfolge[hexwert] = f"{len(reihenfolge) + 1:02d}"
|
||||
return f"{m.group(1)}_{reihenfolge[hexwert]}"
|
||||
|
||||
html = KENNUNG_RE.sub(kennung, html)
|
||||
|
||||
def einbindung(m: re.Match) -> str:
|
||||
url = m.group(1)
|
||||
if url == LEAFLET_JS:
|
||||
return ' <script src="../../assets/leaflet.js"></script>\n'
|
||||
if url == LEAFLET_CSS:
|
||||
return (' <link rel="stylesheet" '
|
||||
'href="../../assets/leaflet.css"/>\n')
|
||||
return ""
|
||||
|
||||
return EINBINDUNG_RE.sub(einbindung, html)
|
||||
|
||||
|
||||
def zeichne_karte(koordinaten, touren, abweichung: float) -> None:
|
||||
import folium
|
||||
|
||||
breite, laenge = projiziere(koordinaten)
|
||||
karte = folium.Map(location=[float(breite.mean()), float(laenge.mean())],
|
||||
zoom_start=9, tiles="OpenStreetMap",
|
||||
control_scale=True)
|
||||
|
||||
for nummer, (tour, farbe) in enumerate(zip(touren, TOURFARBEN), start=1):
|
||||
weg = [[float(breite[s]), float(laenge[s])] for s in tour["stationen"]]
|
||||
folium.PolyLine(
|
||||
weg, color=farbe, weight=3, opacity=0.85,
|
||||
tooltip=(f"Fahrzeug {nummer}: {tour['strecke']} km, "
|
||||
f"{tour['fracht']}/{tour['kapazitaet']} Einheiten")
|
||||
).add_to(karte)
|
||||
|
||||
# Besuchsreihenfolge je Kunde - dieselbe Information, die im SVG als
|
||||
# Linienzug steckt, hier als Zahl am Punkt.
|
||||
reihenfolge = {}
|
||||
for nummer, tour in enumerate(touren, start=1):
|
||||
for stelle, ort in enumerate(tour["stationen"][1:-1], start=1):
|
||||
reihenfolge[ort] = (nummer, stelle)
|
||||
|
||||
for ort in range(1, len(koordinaten)):
|
||||
fahrzeug, stelle = reihenfolge.get(ort, (0, 0))
|
||||
fenster = ZEITFENSTER[ort]
|
||||
folium.CircleMarker(
|
||||
[float(breite[ort]), float(laenge[ort])],
|
||||
radius=4 + 1.4 * BEDARFE[ort],
|
||||
color=TOURFARBEN[fahrzeug - 1] if fahrzeug else "#334155",
|
||||
fill=True, fill_opacity=0.75, weight=2,
|
||||
tooltip=(f"K{ort} — Bedarf {BEDARFE[ort]}, "
|
||||
f"Zeitfenster {fenster[0]}–{fenster[1]} min<br>"
|
||||
f"Fahrzeug {fahrzeug}, Stopp {stelle}"),
|
||||
).add_to(karte)
|
||||
|
||||
folium.CircleMarker(
|
||||
[float(breite[0]), float(laenge[0])], radius=9, color="#be123c",
|
||||
fill=True, fill_opacity=0.9, weight=3,
|
||||
tooltip=f"Depot — Schicht 0–{SCHICHTLAENGE} min",
|
||||
).add_to(karte)
|
||||
|
||||
os.makedirs(KARTEN, exist_ok=True)
|
||||
pfad = os.path.join(KARTEN, f"{KARTENNAME}.html")
|
||||
with open(pfad, "w", encoding="utf-8") as datei:
|
||||
datei.write(normalisiere(karte.get_root().render()))
|
||||
print(f"geschrieben: {pfad} "
|
||||
f"(Projektionsabweichung hoechstens {abweichung:.4f} %)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
koordinaten, distanz = erzeuge_daten()
|
||||
touren = loese(distanz)
|
||||
|
|
@ -189,3 +348,6 @@ if __name__ == "__main__":
|
|||
f"{tour['fracht']}/{tour['kapazitaet']} | {stationen}")
|
||||
print(f"Gesamtstrecke: {gesamt} km")
|
||||
zeichne(koordinaten, touren)
|
||||
breite, laenge = projiziere(koordinaten)
|
||||
zeichne_karte(koordinaten, touren,
|
||||
pruefe_projektion(koordinaten, breite, laenge))
|
||||
|
|
|
|||
430
bilder_04/karten/kap_graphen_vrp_touren.html
Normal file
430
bilder_04/karten/kap_graphen_vrp_touren.html
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<script src="../../assets/leaflet.js"></script>
|
||||
<link rel="stylesheet" href="../../assets/leaflet.css"/>
|
||||
|
||||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_01 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
left: 0.0%;
|
||||
top: 0.0%;
|
||||
}
|
||||
.leaflet-container { font-size: 1rem; }
|
||||
</style>
|
||||
|
||||
<style>html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>#map {
|
||||
position:absolute;
|
||||
top:0;
|
||||
bottom:0;
|
||||
right:0;
|
||||
left:0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
L_NO_TOUCH = false;
|
||||
L_DISABLE_3D = false;
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_01" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_01 = L.map(
|
||||
"map_01",
|
||||
{
|
||||
center: [50.99937073954281, 9.200100716327679],
|
||||
crs: L.CRS.EPSG3857,
|
||||
...{
|
||||
"zoom": 9,
|
||||
"zoomControl": true,
|
||||
"preferCanvas": false,
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
L.control.scale().addTo(map_01);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var tile_layer_02 = L.tileLayer(
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19,
|
||||
"maxNativeZoom": 19,
|
||||
"noWrap": false,
|
||||
"attribution": "\u0026copy; \u003ca href=\"https://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors",
|
||||
"subdomains": "abc",
|
||||
"detectRetina": false,
|
||||
"tms": false,
|
||||
"opacity": 1,
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
tile_layer_02.addTo(map_01);
|
||||
|
||||
|
||||
var poly_line_03 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [50.53528002580979, 9.056780383216372], [50.70020093721884, 9.023183616953922], [50.899334211646234, 8.572820141079644], [50.828627944796516, 8.85527539693994], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#4338ca", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_03.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 1: 172 km, 10/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_04 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.202571214486355, 9.47729598790725], [51.23585035464649, 9.309049094203546], [51.36580069667539, 9.454691406682949], [51.2977516796715, 9.779278159065392], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#0891b2", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_04.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 2: 135 km, 9/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_05 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.12239321211423, 9.61645605992352], [51.0634545325666, 9.57161203934019], [50.81449346507728, 9.470641609414168], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#b45309", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_05.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 3: 70 km, 8/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var poly_line_06 = L.polyline(
|
||||
[[50.8902981044233, 9.493705194696913], [51.32909155311604, 8.916255495545233], [51.37139606353655, 8.517488901092882], [51.108758805458066, 8.607477788041049], [50.553365253206685, 9.181091101072626], [50.67063451777786, 9.498609802394965], [50.8902981044233, 9.493705194696913]],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#059669", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.85, "smoothFactor": 1.0, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
poly_line_06.bindTooltip(
|
||||
`<div>
|
||||
Fahrzeug 4: 242 km, 10/10 Einheiten
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_07 = L.circleMarker(
|
||||
[51.12239321211423, 9.61645605992352],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_07.bindTooltip(
|
||||
`<div>
|
||||
K1 — Bedarf 2, Zeitfenster 30–120 min<br>Fahrzeug 3, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_08 = L.circleMarker(
|
||||
[51.37139606353655, 8.517488901092882],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_08.bindTooltip(
|
||||
`<div>
|
||||
K2 — Bedarf 3, Zeitfenster 60–180 min<br>Fahrzeug 4, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_09 = L.circleMarker(
|
||||
[51.202571214486355, 9.47729598790725],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_09.bindTooltip(
|
||||
`<div>
|
||||
K3 — Bedarf 1, Zeitfenster 100–240 min<br>Fahrzeug 2, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_10 = L.circleMarker(
|
||||
[50.899334211646234, 8.572820141079644],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9.6, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_10.bindTooltip(
|
||||
`<div>
|
||||
K4 — Bedarf 4, Zeitfenster 150–300 min<br>Fahrzeug 1, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_11 = L.circleMarker(
|
||||
[51.32909155311604, 8.916255495545233],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_11.bindTooltip(
|
||||
`<div>
|
||||
K5 — Bedarf 2, Zeitfenster 60–180 min<br>Fahrzeug 4, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_12 = L.circleMarker(
|
||||
[51.23585035464649, 9.309049094203546],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_12.bindTooltip(
|
||||
`<div>
|
||||
K6 — Bedarf 2, Zeitfenster 120–240 min<br>Fahrzeug 2, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_13 = L.circleMarker(
|
||||
[50.70020093721884, 9.023183616953922],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_13.bindTooltip(
|
||||
`<div>
|
||||
K7 — Bedarf 3, Zeitfenster 200–360 min<br>Fahrzeug 1, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_14 = L.circleMarker(
|
||||
[50.553365253206685, 9.181091101072626],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_14.bindTooltip(
|
||||
`<div>
|
||||
K8 — Bedarf 1, Zeitfenster 300–450 min<br>Fahrzeug 4, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_15 = L.circleMarker(
|
||||
[51.0634545325666, 9.57161203934019],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_15.bindTooltip(
|
||||
`<div>
|
||||
K9 — Bedarf 2, Zeitfenster 180–300 min<br>Fahrzeug 3, Stopp 2
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_16 = L.circleMarker(
|
||||
[50.81449346507728, 9.470641609414168],
|
||||
{"bubblingMouseEvents": true, "color": "#b45309", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#b45309", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9.6, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_16.bindTooltip(
|
||||
`<div>
|
||||
K10 — Bedarf 4, Zeitfenster 240–360 min<br>Fahrzeug 3, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_17 = L.circleMarker(
|
||||
[51.2977516796715, 9.779278159065392],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_17.bindTooltip(
|
||||
`<div>
|
||||
K11 — Bedarf 3, Zeitfenster 300–480 min<br>Fahrzeug 2, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_18 = L.circleMarker(
|
||||
[50.67063451777786, 9.498609802394965],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_18.bindTooltip(
|
||||
`<div>
|
||||
K12 — Bedarf 2, Zeitfenster 360–500 min<br>Fahrzeug 4, Stopp 5
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_19 = L.circleMarker(
|
||||
[50.53528002580979, 9.056780383216372],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 5.4, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_19.bindTooltip(
|
||||
`<div>
|
||||
K13 — Bedarf 1, Zeitfenster 60–200 min<br>Fahrzeug 1, Stopp 1
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_20 = L.circleMarker(
|
||||
[51.108758805458066, 8.607477788041049],
|
||||
{"bubblingMouseEvents": true, "color": "#059669", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#059669", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_20.bindTooltip(
|
||||
`<div>
|
||||
K14 — Bedarf 2, Zeitfenster 120–300 min<br>Fahrzeug 4, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_21 = L.circleMarker(
|
||||
[51.36580069667539, 9.454691406682949],
|
||||
{"bubblingMouseEvents": true, "color": "#0891b2", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#0891b2", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 8.2, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_21.bindTooltip(
|
||||
`<div>
|
||||
K15 — Bedarf 3, Zeitfenster 240–400 min<br>Fahrzeug 2, Stopp 3
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_22 = L.circleMarker(
|
||||
[50.828627944796516, 8.85527539693994],
|
||||
{"bubblingMouseEvents": true, "color": "#4338ca", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#4338ca", "fillOpacity": 0.75, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 6.8, "stroke": true, "weight": 2}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_22.bindTooltip(
|
||||
`<div>
|
||||
K16 — Bedarf 2, Zeitfenster 300–550 min<br>Fahrzeug 1, Stopp 4
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
var circle_marker_23 = L.circleMarker(
|
||||
[50.8902981044233, 9.493705194696913],
|
||||
{"bubblingMouseEvents": true, "color": "#be123c", "dashArray": null, "dashOffset": null, "fill": true, "fillColor": "#be123c", "fillOpacity": 0.9, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "opacity": 1.0, "radius": 9, "stroke": true, "weight": 3}
|
||||
).addTo(map_01);
|
||||
|
||||
|
||||
circle_marker_23.bindTooltip(
|
||||
`<div>
|
||||
Depot — Schicht 0–600 min
|
||||
</div>`,
|
||||
{
|
||||
"sticky": true,
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
|
@ -85,6 +85,7 @@ api = [
|
|||
figures = [
|
||||
"plotly>=6.0",
|
||||
"graphviz>=0.20",
|
||||
"folium>=0.19", # die Leaflet-Karte der VRP-Touren
|
||||
]
|
||||
|
||||
dev = [
|
||||
|
|
|
|||
656
web_04/assets/leaflet.css
Normal file
656
web_04/assets/leaflet.css
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
6
web_04/assets/leaflet.js
Normal file
6
web_04/assets/leaflet.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -282,6 +282,13 @@ article li { margin: .3em 0; }
|
|||
border-radius: var(--radius); padding: .6em; overflow-x: auto; }
|
||||
.plotly-figur .plotly-graph-div { min-width: 320px; }
|
||||
|
||||
/* Leaflet-Karte im iframe ({karte:...}). Der Rahmen kommt von hier, nicht vom
|
||||
iframe selbst - dessen Standardrahmen passt zu keinem der beiden Themen. */
|
||||
.karte-figur { display: block; width: 100%; height: 30rem; margin: 1.4em 0;
|
||||
border: 1px solid var(--border); border-radius: var(--radius);
|
||||
background: var(--surface); }
|
||||
@media (max-width: 640px) { .karte-figur { height: 22rem; } }
|
||||
|
||||
/* Der Formel-Uebersetzer ist zweispaltig gedacht: links die Mathematik,
|
||||
rechts der Klartext. Auf schmalen Displays wuerde die Formelspalte die
|
||||
Tabelle sonst aus dem Viewport druecken. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue