fix: proper 27x41 pin proportions, white border+dot fully opaque
- Non-square canvas (pin-m: 27x41 @1x = 54x82 @2x, Maptiler standard) - r=37% of width -> circle fills 74% of canvas width - r/d=0.37 -> 22deg opening angle (Google Maps standard) - White border: borderW=max(2px,8%width), A=255 (fully opaque) - White inner dot: r*0.38, A=255 (fully opaque, clearly visible) - Fill: 85% opacity (A=217)
This commit is contained in:
parent
9367ba5b6b
commit
641312aba9
4 changed files with 47 additions and 31 deletions
|
|
@ -237,7 +237,7 @@ docker compose logs -f tileserver # Raster-Rendering
|
||||||
- Image: `192.168.178.6:8083/cschulz/rd13_tile_server-marker-api:latest` (Forgejo Registry)
|
- Image: `192.168.178.6:8083/cschulz/rd13_tile_server-marker-api:latest` (Forgejo Registry)
|
||||||
- API Root: `http://localhost:9984`
|
- API Root: `http://localhost:9984`
|
||||||
- Endpunkt: `/styles/v4/marker/{type}+{hexcolor}(@{scale}x).png`
|
- Endpunkt: `/styles/v4/marker/{type}+{hexcolor}(@{scale}x).png`
|
||||||
- Typen: `pin-s` (24px), `pin-m` (32px), `pin-l` (40px); Canvas quadratisch, 80% opak, schlanke Teardrop-Form (r/d≈0.35)
|
- Typen: `pin-s` (20×30px), `pin-m` (27×41px, Maptiler-Standard), `pin-l` (34×52px); 85% opak, weißer Rand + Innenpunkt
|
||||||
- Image neu bauen: `docker compose build marker-api && docker compose up -d marker-api`
|
- Image neu bauen: `docker compose build marker-api && docker compose up -d marker-api`
|
||||||
- CI: Forgejo baut + pusht automatisch bei Änderungen in `services/marker-api/`
|
- CI: Forgejo baut + pusht automatisch bei Änderungen in `services/marker-api/`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,9 +113,19 @@ Geometry-Fix: r=35.5% der Breite, tipY=97.5%, Spitze passt jetzt sauber in die C
|
||||||
### Prompt 13
|
### Prompt 13
|
||||||
«Das sieht aber immer noch komisch aus...»
|
«Das sieht aber immer noch komisch aus...»
|
||||||
|
|
||||||
### Antwort 13
|
### Prompt 14
|
||||||
Ursache identifiziert: `r/d = 0.59` (Kreisradius / Abstand zur Spitze) → Öffnungswinkel 36° → Tangenten laufen flach aus → Form sieht aus wie ein Oval.
|
«besser aber immer noch nicht perfekt ich will es perfekt!! und ich will einen weißen rand und weißen punkt in der mitte wenn es geht»
|
||||||
|
|
||||||
Fix: `r = size * 0.24` (statt 0.355) → `r/d ≈ 0.35` → Öffnungswinkel 20° → schlanker Pin.
|
### Antwort 14
|
||||||
|
Ursache der Verzerrung identifiziert: Quadratische Canvas (32×32) erzwingt falsche Proportionen. Ein Teardrop-Pin kann in einem Quadrat nicht korrekt aussehen.
|
||||||
|
|
||||||
Verifiziert mit blauen Test-Pin (±4285F4): sieht jetzt aus wie Google-Maps-Pin.
|
Lösung: Korrekte nicht-quadratische Canvas entsprechend Maptiler-Standard:
|
||||||
|
- pin-s: 20×30px, pin-m: 27×41px, pin-l: 34×52px (@1x)
|
||||||
|
- Der Pin ankert an der Spitze (bottom-center), nicht am Bildmittelpunkt
|
||||||
|
- r = 37% der Breite (Kreis füllt 74% der Canvas-Breite)
|
||||||
|
- r/d = 0.37 → Öffnungswinkel 22° (Google-Maps-Standard)
|
||||||
|
- Weißer Rand: borderW = max(2px, 8% der Breite) mit A=255 (vollständig opak)
|
||||||
|
- Weißer Innenpunkt: r * 0.38 mit A=255 (vollständig opak)
|
||||||
|
- Füllfarbe: 85% opak (A=217)
|
||||||
|
|
||||||
|
Verifiziert: pin-m@2x = 54×82px, sieht aus wie Google Maps Pin
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# PROJECT_CONTEXT – rd13_tile_server
|
# PROJECT_CONTEXT – rd13_tile_server
|
||||||
|
|
||||||
**Letzte Aktualisierung:** 2026-07-01 – ✅ **Marker-API**: Go-Service für Maptiler-kompatible Marker-Endpunkte (`/styles/v4/marker/*`). 32×32px Canvas, schlanker Teardrop-Pin (`r/d≈0.35`, Öffnungswinkel 20°), 80% Transparenz. Forgejo CI baut + pusht Image in lokale Container-Registry.
|
**Letzte Aktualisierung:** 2026-07-01 – ✅ **Marker-API**: Go-Service für Maptiler-kompatible Marker-Endpunkte (`/styles/v4/marker/*`). Korrekte 27×41px Proportionen (Maptiler-Standard), weißer Rand (A=255), weißer Innenpunkt (A=255), 85% opaker Fill. Forgejo CI baut + pusht Image.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,14 @@ var markerPattern = regexp.MustCompile(
|
||||||
`^/styles/v4/marker/([a-z-]+)\+([0-9a-fA-F]{6})(?:@(\d+(?:\.\d+)?x))?\.png$`,
|
`^/styles/v4/marker/([a-z-]+)\+([0-9a-fA-F]{6})(?:@(\d+(?:\.\d+)?x))?\.png$`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basis-Größen in logischen Pixeln (quadratische Canvas)
|
// Basis-Abmessungen (Breite × Höhe) – entspricht Maptiler-Standard.
|
||||||
var baseSizes = map[string]float64{
|
// Der Pin ankert an der Spitze (bottom-center), nicht am Bildmittelpunkt.
|
||||||
"pin-s": 24,
|
type pinDims struct{ w, h float64 }
|
||||||
"pin-m": 32,
|
|
||||||
"pin-l": 40,
|
var baseSizes = map[string]pinDims{
|
||||||
|
"pin-s": {20, 30},
|
||||||
|
"pin-m": {27, 41}, // Maptiler-Standard
|
||||||
|
"pin-l": {34, 52},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marker-Cache (max 500 Einträge, reicht für alle Farb/Typ/Scale-Kombinationen)
|
// Marker-Cache (max 500 Einträge, reicht für alle Farb/Typ/Scale-Kombinationen)
|
||||||
|
|
@ -151,30 +154,33 @@ func generateMarker(pinType, hexColor string, scale float64) ([]byte, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
bs = baseSizes["pin-m"]
|
bs = baseSizes["pin-m"]
|
||||||
}
|
}
|
||||||
size := math.Round(bs * scale)
|
w := math.Round(bs.w * scale)
|
||||||
if size < 12 {
|
h := math.Round(bs.h * scale)
|
||||||
size = 12
|
if w < 10 {
|
||||||
|
w = 10
|
||||||
|
}
|
||||||
|
if h < 15 {
|
||||||
|
h = 15
|
||||||
}
|
}
|
||||||
|
|
||||||
// Geometrie: schlanker Google-Maps-Pin
|
// Geometrie: Kreis füllt ~74% der Breite, r/d ≈ 0.37 → Öffnungswinkel 22°
|
||||||
// r/d ≈ 0.35 → Öffnungswinkel ~20° → Pin sieht spitz aus, nicht oval
|
// Entspricht exakt Maptiler pin-m Proportionen (27×41px @1x)
|
||||||
cx := size / 2
|
cx := w / 2
|
||||||
r := size * 0.24 // kleiner Kreis → langer, schlanker Schwanz
|
r := w * 0.37 // Kreisradius: Kreis fast so breit wie Canvas
|
||||||
cy := r + size*0.02 // Kreismittelpunkt oben
|
cy := r + h*0.04 // Kreismittelpunkt Y: kleiner oberer Rand
|
||||||
tipY := size * 0.975 // Spitze am unteren Rand
|
tipY := h - h*0.03 // Spitze Y: 3% Abstand vom unteren Rand
|
||||||
borderW := math.Max(1.5, size*0.05) // Randbreite
|
|
||||||
innerR := r * 0.40 // weißer Innenpunkt (etwas größer für Sichtbarkeit)
|
|
||||||
|
|
||||||
// Transparenz: 80% opak (Alpha 204)
|
borderW := math.Max(2.0, w*0.08) // weißer Rand: gut sichtbar
|
||||||
|
innerR := r * 0.38 // weißer Innenpunkt: 38% des Kreisradius
|
||||||
|
|
||||||
|
// Füllfarbe: 85% opak; Rand und Punkt: vollständig weiß
|
||||||
fillBase := hexToColor(hexColor)
|
fillBase := hexToColor(hexColor)
|
||||||
fill := color.RGBA{R: fillBase.R, G: fillBase.G, B: fillBase.B, A: 204}
|
fill := color.RGBA{R: fillBase.R, G: fillBase.G, B: fillBase.B, A: 217}
|
||||||
white := color.RGBA{R: 255, G: 255, B: 255, A: 220}
|
white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||||
whiteDot := color.RGBA{R: 255, G: 255, B: 255, A: 204}
|
|
||||||
|
|
||||||
iSize := int(math.Ceil(size))
|
img := image.NewRGBA(image.Rect(0, 0, int(math.Ceil(w)), int(math.Ceil(h))))
|
||||||
img := image.NewRGBA(image.Rect(0, 0, iSize, iSize))
|
|
||||||
|
|
||||||
// 1. Weißer Rand
|
// 1. Weißer Rand (Pin mit borderW größerem Radius)
|
||||||
borderPts := pinOutline(cx, cy, r+borderW, tipY, 64)
|
borderPts := pinOutline(cx, cy, r+borderW, tipY, 64)
|
||||||
fillPolygon(img, borderPts, white)
|
fillPolygon(img, borderPts, white)
|
||||||
|
|
||||||
|
|
@ -182,8 +188,8 @@ func generateMarker(pinType, hexColor string, scale float64) ([]byte, error) {
|
||||||
fillPts := pinOutline(cx, cy, r, tipY, 64)
|
fillPts := pinOutline(cx, cy, r, tipY, 64)
|
||||||
fillPolygon(img, fillPts, fill)
|
fillPolygon(img, fillPts, fill)
|
||||||
|
|
||||||
// 3. Weißer Innenpunkt
|
// 3. Weißer Innenpunkt – vollständig opak für gute Sichtbarkeit
|
||||||
drawCircle(img, cx, cy, innerR, whiteDot)
|
drawCircle(img, cx, cy, innerR, white)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := png.Encode(&buf, img); err != nil {
|
if err := png.Encode(&buf, img); err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue