Skip to content

Fix: HexagonDrawable 4-arg constructor renders invisible hexagons#1301

Merged
devemux86 merged 1 commit into
mapsforge:masterfrom
jhotadhari:fix/hexagon-drawable-mercator
Jul 1, 2026
Merged

Fix: HexagonDrawable 4-arg constructor renders invisible hexagons#1301
devemux86 merged 1 commit into
mapsforge:masterfrom
jhotadhari:fix/hexagon-drawable-mercator

Conversation

@jhotadhari

Copy link
Copy Markdown

Closes #1300

The bug

HexagonDrawable has two constructors that build geometry in different coordinate spaces, causing the 4-argument version to render invisible hexagons.

The 2-arg constructor correctly uses geographic lat/lng when building the JTS polygon via GeomBuilder:

// 2-arg constructor (works correctly)
GeoPoint point = findGeoPointWithGivenDistance(center, i * Math.PI / 3, radiusKm);
gb.points(point.getLongitude(), point.getLatitude());  // geographic degrees

The 4-arg constructor was projecting through Mercator first and feeding screen-space x/y into GeomBuilder as if they were lat/lng:

// 4-arg constructor (broken — before the fix)
GeoPoint point = findGeoPointWithGivenDistance(center, rotationRad + i * Math.PI / 3, radiusKm);
MercatorProjection.project(point, tmp);
gb.points(tmp.x, tmp.y);  // Mercator pixels, NOT geographic degrees!

Since VectorLayer later interprets whatever it receives as geographic coordinates, the Mercator-projected hexagon lands at a completely wrong map position and is effectively invisible.

This was the only usage of MercatorProjection in the entire geometries package — other style-accepting drawables (CircleDrawable, RectangleDrawable, PolygonDrawable) all pass raw lon/lat directly.

The fix

Remove the MercatorProjection.project() call and Point usage from the 4-arg constructor. Instead, pass geographic coordinates directly to GeomBuilder, matching the 2-arg constructor behavior:

// 4-arg constructor (fixed)
GeoPoint point = findGeoPointWithGivenDistance(center, rotationRad + i * Math.PI / 3, radiusKm);
gb.points(point.getLongitude(), point.getLatitude());  // geographic degrees

Also removed the now-unused MercatorProjection and Point imports.

Proof

HexagonDrawableTest renders 4 hexagons at distinct European locations and logs every vertex coordinate. All vertices are valid geographic degrees, confirming the 4-arg constructor now correctly places hexagons at the intended map positions:

--- 2-arg constructor (BLUE) — center: [lat=52.0,lon=5.0] ---
  Hexagon is built from 6 geographic coordinates:
    vertex 0: lon=5.000000 lat=56.496600 — OK: YES
    vertex 1: lon=11.644154 lat=54.068501 — OK: YES
    vertex 2: lon=11.012326 lat=49.592311 — OK: YES
    vertex 3: lon=5.000000 lat=47.503399 — OK: YES
    vertex 4: lon=-1.012326 lat=49.592311 — OK: YES
    vertex 5: lon=-1.644154 lat=54.068501 — OK: YES
--- 4-arg constructor 0° rotation (GREEN) — center: [lat=52.0,lon=12.0] ---
  Hexagon is built from 6 geographic coordinates:
    vertex 0: lon=12.000000 lat=56.496600 — OK: YES
    vertex 1: lon=18.644154 lat=54.068501 — OK: YES
    vertex 2: lon=18.012326 lat=49.592311 — OK: YES
    vertex 3: lon=12.000000 lat=47.503399 — OK: YES
    vertex 4: lon=5.987673 lat=49.592311 — OK: YES
    vertex 5: lon=5.355845 lat=54.068501 — OK: YES
--- 4-arg constructor 30° rotation (RED) — center: [lat=46.0,lon=8.0] ---
  Hexagon is built from 6 geographic coordinates:
    vertex 0: lon=11.484998 lat=49.843906 — OK: YES
    vertex 1: lon=14.458934 lat=45.817676 — OK: YES
    vertex 2: lon=11.026736 lat=42.064046 — OK: YES
    vertex 3: lon=4.973263 lat=42.064046 — OK: YES
    vertex 4: lon=1.541065 lat=45.817676 — OK: YES
    vertex 5: lon=4.515001 lat=49.843906 — OK: YES
--- 4-arg constructor 60° rotation, 250km (MAGENTA) — center: [lat=42.0,lon=12.0] ---
  Hexagon is built from 6 geographic coordinates:
    vertex 0: lon=14.666647 lat=43.093611 — OK: YES
    vertex 1: lon=14.574145 lat=40.846796 — OK: YES
    vertex 2: lon=12.000000 lat=39.751699 — OK: YES
    vertex 3: lon=9.425854 lat=40.846796 — OK: YES
    vertex 4: lon=9.333352 lat=43.093611 — OK: YES
    vertex 5: lon=12.000000 lat=44.248300 — OK: YES

Before the fix, the 4-arg constructor would have produced Mercator-projected x/y values (on the order of millions) fed into GeomBuilder as if they were geographic degrees — making those same hexagons land nowhere near the map viewport (effectively invisible).

Side note: running the playground test

The playground build.gradle needed two temporary tweaks to run the test on Ubuntu 22.04 — neither is part of this PR:

  1. LWJGL version: Swapped implementation project(':vtm-desktop-lwjgl')':vtm-desktop-lwjgl3'. LWJGL 2's native .so loading hits a glibc ld.so assertion on Ubuntu 22.04+. LWJGL 3 has no such issue.
  2. Main class wiring: Added mainClass = project.findProperty('mainClassName') so the application plugin picks up the main class from -PmainClassName= on the command line.

Run with:

./gradlew :vtm-playground:run -PmainClassName=org.oscim.test.HexagonDrawableTest

🤖 Generated with Claude Code

…psforge#1300)

The 4-arg constructor was projecting geographic coordinates through
MercatorProjection.project() and feeding the resulting screen-space x/y
values into GeomBuilder.points(). Since VectorLayer interprets whatever
it receives as lat/lng, the Mercator-projected hexagon landed at a
completely wrong map position and was effectively invisible.

Fix: remove MercatorProjection.project() call and Point usage, pass
point.getLongitude()/point.getLatitude() directly to gb.points(),
matching the working 2-arg constructor. Removed unused imports.

Add HexagonDrawableTest to the playground, which renders 4 hexagons at
distinct European locations and logs vertex coordinates to console,
proving all vertices are valid geographic degrees.

Co-Authored-By: Claude <noreply@anthropic.com>
@devemux86

Copy link
Copy Markdown
Collaborator

LWJGL version: Swapped implementation project(':vtm-desktop-lwjgl') → ':vtm-desktop-lwjgl3'. LWJGL 2's native .so loading hits a glibc ld.so assertion on Ubuntu 22.04+. LWJGL 3 has no such issue.

Testing on Debian with Intel GPU, it works correctly.

Perhaps something with your graphics drivers?
Anyway developers / users can modify the project to work better on their desktops.

@jhotadhari

Copy link
Copy Markdown
Author

Perhaps something with your graphics drivers?

could be. changing to vtm-desktop-lwjgl3 locally for testing for me is alright.
and o android (where I want to use the library) I dont run into this issue.

Keep in mind that the the issue generation, the PR, the fix, all description and report ... is all generated by claude and I have no idea about java.

This described fix described above appears to me that it might break some apps, that already implemented a workaround for the hexagon. Im not sure if it is good to roll out this fix

@devemux86 devemux86 added the bug label Jul 1, 2026
@devemux86 devemux86 added this to the 0.29.0 milestone Jul 1, 2026
@devemux86 devemux86 merged commit 66d69cc into mapsforge:master Jul 1, 2026
1 check passed
@devemux86

Copy link
Copy Markdown
Collaborator

@jhotadhari Thanks for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HexagonDrawable 4-arg constructor produces invisible hexagons due to inconsistent coordinate projection vs all other Drawables

2 participants