Update map generator for federal election.

This commit is contained in:
Kim Taylor committed 2025-03-03 21:05:24 +11:00
1 parent 2a0ba8eed2
commit 608ed0f92c
4 files changed
+156 -3

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
\****************/ \****************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var polylabel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! polylabel */ \"./node_modules/polylabel/polylabel.js\");\n\r\n\r\nfunction normaliseCouncilName(str) {\r\n const regex = /(.*?)(?:(?: Rural)?(?: City| Shire| Borough) Council)/g;\r\n const matches = str.matchAll(regex);\r\n\r\n // If we get a match, convert to slug format\r\n for (const match of matches) {\r\n return match[1].toLowerCase().replace(\" \", \"-\");\r\n }\r\n\r\n // If we didn't find any matches, try convert input to slug format\r\n return str.toLowerCase().replace(\" \", \"-\");\r\n};\r\n\r\nconst searchParams = new URLSearchParams(window.location.search);\r\nconst councilName = normaliseCouncilName(searchParams.get(\"council\"));\r\nconsole.log(councilName);\r\n\r\nmapboxgl.accessToken = 'pk.eyJ1IjoibWF0dHl3YXkiLCJhIjoiY2x6eG9vMzZyMHY2cDJqb3M1ODZnNjF4cyJ9.IX8CfYQZUaQhSjWgMXmsEA';\r\nconst map = new mapboxgl.Map({\r\n container: 'map',\r\n zoom: 10,\r\n style: 'mapbox://styles/mattyway/cm03vw57q00fd01pn93wf2j7p',\r\n center: [145.00724,-37.79011]\r\n});\r\n\r\nmap.on('load', () => {\r\n // Load an image from an external URL.\r\n map.loadImage('circle.png', (error, image) => {\r\n if (error) throw error;\r\n\r\n map.addImage('blue-circle', image);\r\n\r\n fetch(\"wards_withboundaries.json\")\r\n .then(response => {\r\n response.json()\r\n .then((wardData) => {\r\n const filteredWardData = wardData.filter((ward) => normaliseCouncilName(ward.parentElectorateName) == councilName);\r\n\r\n var bounds = {\r\n \"west\": undefined,\r\n \"south\": undefined,\r\n \"east\": undefined,\r\n \"north\": undefined\r\n }\r\n\r\n function addToBounds(coordinate) {\r\n if (bounds.west == undefined || coordinate[0] < bounds.west) {\r\n bounds.west = coordinate[0];\r\n }\r\n\r\n if (bounds.south == undefined || coordinate[1] < bounds.south) {\r\n bounds.south = coordinate[1];\r\n }\r\n\r\n if (bounds.east == undefined || coordinate[0] > bounds.east) {\r\n bounds.east = coordinate[0];\r\n }\r\n\r\n if (bounds.north == undefined || coordinate[1] > bounds.north) {\r\n bounds.north = coordinate[1];\r\n }\r\n }\r\n\r\n var labelFeatures = [];\r\n\r\n filteredWardData.forEach(wardData => {\r\n const featureCollection = {\r\n 'type': 'FeatureCollection',\r\n 'features': [\r\n {\r\n 'type': 'Feature',\r\n 'geometry': JSON.parse(wardData.boundaryJson)\r\n }\r\n ]\r\n };\r\n\r\n if (featureCollection.features[0].geometry.type == \"Polygon\") {\r\n featureCollection.features[0].geometry.coordinates[0].forEach(coordinate => {\r\n addToBounds(coordinate);\r\n });\r\n }\r\n if (featureCollection.features[0].geometry.type == \"MultiPolygon\") {\r\n featureCollection.features[0].geometry.coordinates.forEach(polygon => {\r\n polygon[0].forEach(coordinate => {\r\n addToBounds(coordinate);\r\n });\r\n });\r\n }\r\n\r\n // Add data\r\n map.addSource(\"data_\"+wardData.electorateId, {\r\n 'type': 'geojson',\r\n 'data': featureCollection\r\n });\r\n\r\n // Add a line along the data\r\n map.addLayer({\r\n 'id': \"outline_\"+wardData.electorateId,\r\n 'type': 'line',\r\n 'source': \"data_\"+wardData.electorateId,\r\n 'layout': {},\r\n 'paint': {\r\n 'linLine truncated eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var polylabel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! polylabel */ \"./node_modules/polylabel/polylabel.js\");\n\r\n\r\nfunction normaliseCouncilName(str) {\r\n const regex = /(.*?)(?:(?: Rural)?(?: City| Shire| Borough) Council)/g;\r\n const matches = str.matchAll(regex);\r\n\r\n // If we get a match, convert to slug format\r\n for (const match of matches) {\r\n return match[1].toLowerCase().replace(\" \", \"-\");\r\n }\r\n\r\n // If we didn't find any matches, try convert input to slug format\r\n return str.toLowerCase().replace(\" \", \"-\");\r\n};\r\n\r\nconst searchParams = new URLSearchParams(window.location.search);\r\nconst councilName = normaliseCouncilName(searchParams.get(\"council\"));\r\nconsole.log(councilName);\r\n\r\nmapboxgl.accessToken = 'pk.eyJ1IjoibWF0dHl3YXkiLCJhIjoiY2x6eG9vMzZyMHY2cDJqb3M1ODZnNjF4cyJ9.IX8CfYQZUaQhSjWgMXmsEA';\r\nconst map = new mapboxgl.Map({\r\n container: 'map',\r\n zoom: 10,\r\n style: 'mapbox://styles/mattyway/cm03vw57q00fd01pn93wf2j7p',\r\n center: [145.00724,-37.79011]\r\n});\r\n\r\nmap.on('load', () => {\r\n // Load an image from an external URL.\r\n map.loadImage('circle.png', (error, image) => {\r\n if (error) throw error;\r\n\r\n map.addImage('blue-circle', image);\r\n\r\n fetch(\"federal_2025_boundaries.json\")\r\n .then(response => {\r\n response.json()\r\n .then((wardData) => {\r\n const filteredWardData = wardData.filter((ward) => normaliseCouncilName(ward.parentElectorateName) == councilName);\r\n\r\n var bounds = {\r\n \"west\": undefined,\r\n \"south\": undefined,\r\n \"east\": undefined,\r\n \"north\": undefined\r\n }\r\n\r\n function addToBounds(coordinate) {\r\n if (bounds.west == undefined || coordinate[0] < bounds.west) {\r\n bounds.west = coordinate[0];\r\n }\r\n\r\n if (bounds.south == undefined || coordinate[1] < bounds.south) {\r\n bounds.south = coordinate[1];\r\n }\r\n\r\n if (bounds.east == undefined || coordinate[0] > bounds.east) {\r\n bounds.east = coordinate[0];\r\n }\r\n\r\n if (bounds.north == undefined || coordinate[1] > bounds.north) {\r\n bounds.north = coordinate[1];\r\n }\r\n }\r\n\r\n var labelFeatures = [];\r\n\r\n filteredWardData.forEach(wardData => {\r\n const featureCollection = {\r\n 'type': 'FeatureCollection',\r\n 'features': [\r\n {\r\n 'type': 'Feature',\r\n 'geometry': JSON.parse(wardData.boundaryJson)\r\n }\r\n ]\r\n };\r\n\r\n if (featureCollection.features[0].geometry.type == \"Polygon\") {\r\n featureCollection.features[0].geometry.coordinates[0].forEach(coordinate => {\r\n addToBounds(coordinate);\r\n });\r\n }\r\n if (featureCollection.features[0].geometry.type == \"MultiPolygon\") {\r\n featureCollection.features[0].geometry.coordinates.forEach(polygon => {\r\n polygon[0].forEach(coordinate => {\r\n addToBounds(coordinate);\r\n });\r\n });\r\n }\r\n\r\n // Add data\r\n map.addSource(\"data_\"+wardData.electorateId, {\r\n 'type': 'geojson',\r\n 'data': featureCollection\r\n });\r\n\r\n // Add a line along the data\r\n map.addLayer({\r\n 'id': \"outline_\"+wardData.electorateId,\r\n 'type': 'line',\r\n 'source': \"data_\"+wardData.electorateId,\r\n 'layout': {},\r\n 'paint': {\r\n 'Line truncated
/***/ }), /***/ }),
+11
View File
@@ -0,0 +1,11 @@
SHPFILE := ../../../spl-data/federal_2025/aec_data/E_VIC24_region.shp
SHAPELIB := ../../../lib/shapelib-1.6.0
../federal_2025_boundaries.json: $(SHPFILE) shptojson
LD_LIBRARY_PATH=$(SHAPELIB)/.libs ./shptojson $(SHPFILE) > $@
CFLAGS := -I $(SHAPELIB)
LDFLAGS := -L $(SHAPELIB)/.libs
shptojson: shptojson.c
gcc -Wall $(CFLAGS) $(LDFLAGS) -lshp $< -o $@
+142
View File
@@ -0,0 +1,142 @@
/******************************************************************************
*
* Project: Shapelib
* Purpose: Sample application for dumping contents of a shapefile to
* the terminal in human readable form.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 1999, Frank Warmerdam
*
* SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
******************************************************************************
*
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "shapefil.h"
int main(int argc, char **argv)
{
int nPrecision = 15;
int first;
const char *divison_name;
/* -------------------------------------------------------------------- */
/* Display a usage message. */
/* -------------------------------------------------------------------- */
if (argc != 2)
{
printf("Usage: shptojson shp_file\n");
exit(1);
}
/* -------------------------------------------------------------------- */
/* Open the passed shapefile. */
/* -------------------------------------------------------------------- */
SHPHandle hSHP = SHPOpen(argv[1], "rb");
DBFHandle hDBF = DBFOpen(argv[1], "rb");
if (hSHP == NULL || hDBF == NULL)
{
printf("Unable to open:%s\n", argv[1]);
exit(1);
}
/* -------------------------------------------------------------------- */
/* Print out the file bounds. */
/* -------------------------------------------------------------------- */
int nEntities;
int nShapeType;
double adfMinBound[4];
double adfMaxBound[4];
SHPGetInfo(hSHP, &nEntities, &nShapeType, adfMinBound, adfMaxBound);
fprintf(stderr, "Shapefile Type: %s # of Shapes: %d\n\n",
SHPTypeName(nShapeType), nEntities);
/* -------------------------------------------------------------------- */
/* Skim over the list of shapes, printing all the vertices. */
/* -------------------------------------------------------------------- */
printf("[\n");
for (int i = 0; i < nEntities; i++)
{
SHPObject *psShape = SHPReadObject(hSHP, i);
if (psShape == NULL)
{
fprintf(stderr,
"Unable to read shape %d, terminating object reading.\n",
i);
break;
}
divison_name = DBFReadStringAttribute(hDBF, i, 1);
fprintf(stderr, "shape %s has %i parts\n", divison_name, psShape->nParts);
printf(" {\n");
printf(" \"electorateId\": \"%08i\",\n", i);
printf(" \"electorateName\": \"%s\",\n", divison_name);
printf(" \"electorateType\": \"Division\",\n");
printf(" \"parentElectorateName\": \"%s\",\n", divison_name);
printf(" \"boundaryJson\": \"{\\\"type\\\": ");
if (psShape->nParts != 1)
{
printf("\\\"MultiPolygon\\\",\\\"coordinates\\\":[[[");
}
else
{
printf("\\\"Polygon\\\",\\\"coordinates\\\":[[");
}
first = 1;
for (int j = 0, iPart = 1; j < psShape->nVertices; j++)
{
if (iPart < psShape->nParts && psShape->panPartStart[iPart] == j)
{
iPart++;
printf("]],[[");
}
else if (!first)
{
printf(",");
}
first = 0;
printf("[%.*g,%.*g]", nPrecision, psShape->padfX[j],
nPrecision, psShape->padfY[j]);
}
if (i == (nEntities-1))
{
if (psShape->nParts != 1)
{
printf("]]]}\"\n }\n");
}
else
{
printf("]]}\"\n }\n");
}
}
else
{
if (psShape->nParts != 1)
{
printf("]]]}\"\n },\n");
}
else
{
printf("]]}\"\n },\n");
}
}
SHPDestroyObject(psShape);
}
printf("]\n");
SHPClose(hSHP);
DBFClose(hDBF);
exit(0);
}
+2 -2
View File
@@ -32,7 +32,7 @@ map.on('load', () => {
map.addImage('blue-circle', image); map.addImage('blue-circle', image);
fetch("wards_withboundaries.json") fetch("federal_2025_boundaries.json")
.then(response => { .then(response => {
response.json() response.json()
.then((wardData) => { .then((wardData) => {
@@ -117,7 +117,7 @@ map.on('load', () => {
// //
// The 2024 set of boundaries only uses 2 MultiPolygon objects (Cathedral in Murrindindi Shire Council and Island in Bass Coast Shire Council) // The 2024 set of boundaries only uses 2 MultiPolygon objects (Cathedral in Murrindindi Shire Council and Island in Bass Coast Shire Council)
// Luckily, the second polygon for both objects results in a good label placement. // Luckily, the second polygon for both objects results in a good label placement.
centrePoint = polylabel(featureCollection.features[0].geometry.coordinates[1], 0.000001); centrePoint = polylabel(featureCollection.features[0].geometry.coordinates[0], 0.000001);
} }