From 5f0efde9838e646ad47515bd17cf1c87547d7628 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Wed, 29 Jul 2026 14:40:23 -0700 Subject: [PATCH] fix(web): respect fill in Canvas hit testing Patch Leaflet's Canvas hit testing so unfilled circles and polygons only capture pointer events along their visible strokes. This allows interactions with overlapping markers through transparent interiors and keeps circle, polygon, rectangle, and ellipse behavior consistent. Fixes #15 --- web/bun.lock | 3 ++ web/package.json | 3 ++ web/patches/leaflet@1.9.4.patch | 82 +++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 web/patches/leaflet@1.9.4.patch diff --git a/web/bun.lock b/web/bun.lock index 57538a6a..405d7cb3 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -14,6 +14,9 @@ }, }, }, + "patchedDependencies": { + "leaflet@1.9.4": "patches/leaflet@1.9.4.patch", + }, "packages": { "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], diff --git a/web/package.json b/web/package.json index 3a273ac0..0f410984 100644 --- a/web/package.json +++ b/web/package.json @@ -16,5 +16,8 @@ "oxlint": "^1.75.0", "oxlint-tsgolint": "^7.0.2001", "vite": "^8.1.5" + }, + "patchedDependencies": { + "leaflet@1.9.4": "patches/leaflet@1.9.4.patch" } } diff --git a/web/patches/leaflet@1.9.4.patch b/web/patches/leaflet@1.9.4.patch new file mode 100644 index 00000000..5f6a9d5f --- /dev/null +++ b/web/patches/leaflet@1.9.4.patch @@ -0,0 +1,82 @@ +diff --git a/dist/leaflet-src.esm.js b/dist/leaflet-src.esm.js +index 8f91d5ddfc78ef05576efd425aac920040a28c94..fb3707421854172d680c32e9a8b4465c956468fc 100644 +--- a/dist/leaflet-src.esm.js ++++ b/dist/leaflet-src.esm.js +@@ -8329,7 +8329,11 @@ var CircleMarker = Path.extend({ + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { +- return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); ++ var tolerance = this._clickTolerance(), ++ distance = p.distanceTo(this._point); ++ ++ return distance <= this._radius + tolerance && ++ (this.options.fill || distance >= this._radius - tolerance); + } + }); + +@@ -8888,7 +8892,7 @@ var Polygon = Polyline.extend({ + } + + // also check if it's on polygon stroke +- return inside || Polyline.prototype._containsPoint.call(this, p, true); ++ return (this.options.fill && inside) || Polyline.prototype._containsPoint.call(this, p, true); + } + + }); +diff --git a/dist/leaflet-src.js b/dist/leaflet-src.js +index 90f6db30412aa1219cba21fccd00644b994a51c8..4a20b97f506f1125d20b9f7278ffa3f065f86991 100644 +--- a/dist/leaflet-src.js ++++ b/dist/leaflet-src.js +@@ -8335,7 +8335,11 @@ + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { +- return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); ++ var tolerance = this._clickTolerance(), ++ distance = p.distanceTo(this._point); ++ ++ return distance <= this._radius + tolerance && ++ (this.options.fill || distance >= this._radius - tolerance); + } + }); + +@@ -8894,7 +8898,7 @@ + } + + // also check if it's on polygon stroke +- return inside || Polyline.prototype._containsPoint.call(this, p, true); ++ return (this.options.fill && inside) || Polyline.prototype._containsPoint.call(this, p, true); + } + + }); +diff --git a/src/layer/vector/CircleMarker.js b/src/layer/vector/CircleMarker.js +index e08b2afd4ec42f7f6b4c589d942bee8bd8ea3594..906f488002bdf5e7409877d8d84aefbae654fb9e 100644 +--- a/src/layer/vector/CircleMarker.js ++++ b/src/layer/vector/CircleMarker.js +@@ -97,7 +97,11 @@ export var CircleMarker = Path.extend({ + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { +- return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); ++ var tolerance = this._clickTolerance(), ++ distance = p.distanceTo(this._point); ++ ++ return distance <= this._radius + tolerance && ++ (this.options.fill || distance >= this._radius - tolerance); + } + }); + +diff --git a/src/layer/vector/Polygon.js b/src/layer/vector/Polygon.js +index 3c4bf04ace32f9eb6caf47980e83f15f6ad318b7..81a88fa7ec4a4f59c582518893a92feb25b0fabb 100644 +--- a/src/layer/vector/Polygon.js ++++ b/src/layer/vector/Polygon.js +@@ -147,7 +147,7 @@ export var Polygon = Polyline.extend({ + } + + // also check if it's on polygon stroke +- return inside || Polyline.prototype._containsPoint.call(this, p, true); ++ return (this.options.fill && inside) || Polyline.prototype._containsPoint.call(this, p, true); + } + + });