I’m working on a project that integrates D3.js with Livewire (v3.4) in a Laravel application. I aim to display points (schools) on a map when a region (polygon) is clicked and zoomed in. The points should load dynamically based on the region’s desa_code
using a Livewire method.
The issue I’m facing is that while the map is zoomed and the popup with region information displays correctly, the points do not appear on the canvas, and the regions sometimes disappear.
Here’s a summary of my process:
- Map and regions (polygons) load correctly from a GeoJSON file.
- On clicking a region, it zooms in and calls the Livewire method to load the corresponding points (schools).
- Points should be drawn on the map after the region is zoomed in, but they don’t appear.
Code Overview:
1. Livewire Component (PHP)
#[On('loadPoints')]
public function loadPoints($desaCode)
{
$this->points = AppModelsMapHistory::getSekolahsWithinDesa($desaCode);
$this->dispatchBrowserEvent('pointsLoaded', ['points' => $this->points]);
}
2. JavaScript (D3 and Livewire Integration)
function clicked(event, d) {
event.stopPropagation();
if (active === this) {
return reset();
}
active = this;
features.transition().duration(500)
.style("opacity", function() {
return this === active ? 1 : 0;
});
const [[x0, y0], [x1, y1]] = path.bounds(d);
svg.transition().duration(1000).call(
zoom.transform,
d3.zoomIdentity.translate(width / 2, height / 2)
.scale(Math.min(8, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height)))
.translate(-(x0 + x1) / 2, -(y0 + y1) / 2)
);
// Load points (schools) based on desaCode
Livewire.emit('loadPoints', d.properties.desa_code);
}
Livewire.on('pointsLoaded', (event) => {
var points = JSON.parse(event.points);
var pointsLayer = d3.select("#map").select("svg").append("g");
pointsLayer.selectAll("circle").remove();
pointsLayer.selectAll("circle")
.data(points.features)
.enter().append("circle")
.attr("cx", function(d) {
return projection([d.geometry.coordinates[0], d.geometry.coordinates[1]])[0];
})
.attr("cy", function(d) {
return projection([d.geometry.coordinates[0], d.geometry.coordinates[1]])[1];
})
.attr("r", 5)
.attr("fill", "blue")
.on("click", function(d) {
alert(`Sekolah: ${d.properties.name}nBentuk: ${d.properties.bentuk}nPegawai: ${d.properties.pegawai_count}`);
});
});
3. Complete Code
Here’s the complete code it works fine when I zoom the region, but I would like the points to appear inside the zoomed region.
function showInfoPopup(event, properties) {
d3.select("#info-popup").remove();
var popup = d3.select("body").append("div")
.attr("id", "info-popup")
.style("position", "absolute")
.style("background", "white")
.style("border", "1px solid #ccc")
.style("padding", "10px")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY + 10) + "px");
// Add close button
popup.append("button")
.text("Close")
.on("click", function () {
d3.select("#info-popup").remove();
});
popup.append("h4").text("Informasi Wilayah");
popup.append("p").text(`Desa: ${properties.desa_name}`);
popup.append("p").text(`Kecamatan: ${properties.kecamatan_name}`);
}
Issue:
- Regions sometimes disappear on Zoom, and points do not appear on the canvas.
- The
pointsLoaded
event returns the correct data (checked withconsole.log(points)
), but the points aren’t rendered. - I suspect there may be an issue with how the points are layered or attached to the canvas after Zoom.
What I’ve Tried:
- Verified that points are returned correctly from Livewire.
- Used D3’s
.data()
to bind the points data tocircle
elements. - Tried different projection methods and
cx
/cy
calculations.
Any help on how to correctly render the points and maintain the regions on Zoom would be greatly appreciated!