fix: marker pin 32x32 canvas, correct geometry, 80% opacity

- Square canvas (pin-m=32x32 @1x, 64x64 @2x) - fits MediaWiki slot
- r=35.5% of width, tipY=97.5%: teardrop no longer clipped
- 80% transparency: fill.A=204, white border A=220, inner dot A=204
This commit is contained in:
Conrad Schulz 2026-07-01 12:13:48 +00:00
parent 9df190aca8
commit 3960c9c5c1
4 changed files with 45 additions and 27 deletions

View file

@ -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)
- API Root: `http://localhost:9984`
- Endpunkt: `/styles/v4/marker/{type}+{hexcolor}(@{scale}x).png`
- Typen: `pin-s` (24px), `pin-m` (32px), `pin-l` (40px)
- Typen: `pin-s` (24px), `pin-m` (32px), `pin-l` (40px); Canvas quadratisch, 80% opak
- 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/`

View file

@ -95,10 +95,24 @@ Aufgeräumt:
- `docker-compose.yml` Image-Referenz aktualisiert
- Session-History und PROJECT_CONTEXT erstellt/aktualisiert
Gesamtstruktur:
```
rd13_tile_server (Forgejo Repo)
├── services/marker-api/ ← Go Code + Dockerfile
├── .forgejo/workflows/ ← CI baut + pusht nach Registry
└── docker-compose.yml ← nutzt Registry-Image
```
### Prompt 11
«so sehen die marker aus im media wiki warum sind sie farblos und warum abgeschnitten?»
### Antwort 11
Zwei Probleme identifiziert:
1. **Abgeschnitten**: Canvas war quadratisch, Teardrop-Spitze wurde unten abgeschnitten
2. **Farblos**: `7e7e7e` ist das MediaWiki-Default wenn kein `marker-color` im GeoJSON gesetzt ist korrektes Verhalten
Geometry-Fix: r=35.5% der Breite, tipY=97.5%, Spitze passt jetzt sauber in die Canvas.
---
### Prompt 12
«Aber ich will doch das es in media wiki gut aussieht also muss es in 32x32 passen und gut aussehen. wäre es auch möglich ihn etwas durchsichtig zu machen?»
### Antwort 12
Zurück auf quadratische Canvas (pin-m=32×32 @1x, 64×64 @2x) mit optimierter Geometrie:
- `r=35.5%` der Breite (statt 29%) → Kreis füllt Canvas besser aus
- `tipY=97.5%` → Spitze immer noch vollständig sichtbar
- 80% Transparenz: `fill.A=204`, `white.A=220`
- Verifiziert: `pin-m@2x = 64×64px PNG`

View file

@ -1,6 +1,6 @@
# PROJECT_CONTEXT rd13_tile_server
**Letzte Aktualisierung:** 2026-07-01 **Marker-API**: Go-Service für Maptiler-kompatible Marker-Endpunkte (`/styles/v4/marker/*`) hinzugefügt. 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/*`) hinzugefügt. 32×32px Canvas, 80% Transparenz, Google-Maps-Teardrop-Form. Forgejo CI baut + pusht Image in lokale Container-Registry.
---

View file

@ -21,7 +21,7 @@ var markerPattern = regexp.MustCompile(
`^/styles/v4/marker/([a-z-]+)\+([0-9a-fA-F]{6})(?:@(\d+(?:\.\d+)?x))?\.png$`,
)
// Basis-Größen in logischen Pixeln
// Basis-Größen in logischen Pixeln (quadratische Canvas)
var baseSizes = map[string]float64{
"pin-s": 24,
"pin-m": 32,
@ -147,30 +147,34 @@ func generateMarker(pinType, hexColor string, scale float64) ([]byte, error) {
}
markerCacheMu.RUnlock()
baseSize := baseSizes[pinType]
if baseSize == 0 {
baseSize = 32
bs, ok := baseSizes[pinType]
if !ok {
bs = baseSizes["pin-m"]
}
size := math.Round(baseSize * scale)
if size < 10 {
size = 10
size := math.Round(bs * scale)
if size < 12 {
size = 12
}
// Geometrie-Parameter
// Geometrie: Teardrop in quadratische Canvas einpassen
// Kreis füllt obere 3/4, Spitze landet am unteren Rand
cx := size / 2
r := size * 0.29 // Kreisradius
cy := r + size*0.03 // Kreismittelpunkt Y
tipY := size * 0.96 // Spitze Y
borderW := math.Max(1.5, size*0.04) // Randbreite
innerR := r * 0.38 // weißer Innenpunkt (Google-Maps-Look)
r := size * 0.355 // Kreisradius (rechts/links je 1px Puffer für Rand)
cy := r + size*0.02 // Kreismittelpunkt Y (kleiner oberer Rand)
tipY := size * 0.975 // Spitze Y (knapp vor unterem Rand)
borderW := math.Max(1.2, size*0.045) // Randbreite
innerR := r * 0.36 // weißer Innenpunkt
fill := hexToColor(hexColor)
white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
// Transparenz: 80% opak (Alpha 204)
fillBase := hexToColor(hexColor)
fill := color.RGBA{R: fillBase.R, G: fillBase.G, B: fillBase.B, A: 204}
white := color.RGBA{R: 255, G: 255, B: 255, A: 220}
whiteDot := color.RGBA{R: 255, G: 255, B: 255, A: 204}
iSize := int(math.Ceil(size))
img := image.NewRGBA(image.Rect(0, 0, iSize, iSize))
// 1. Weißer Rand (Pin mit leicht größerem Radius)
// 1. Weißer Rand
borderPts := pinOutline(cx, cy, r+borderW, tipY, 64)
fillPolygon(img, borderPts, white)
@ -178,8 +182,8 @@ func generateMarker(pinType, hexColor string, scale float64) ([]byte, error) {
fillPts := pinOutline(cx, cy, r, tipY, 64)
fillPolygon(img, fillPts, fill)
// 3. Weißer Innenpunkt (klassischer Google-Maps-Stil)
drawCircle(img, cx, cy, innerR, white)
// 3. Weißer Innenpunkt
drawCircle(img, cx, cy, innerR, whiteDot)
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {