diff --git a/map-generator/circle.png b/map-generator/circle.png new file mode 100644 index 0000000..412e60a Binary files /dev/null and b/map-generator/circle.png differ diff --git a/map-generator/circle.svg b/map-generator/circle.svg new file mode 100644 index 0000000..9b579a7 --- /dev/null +++ b/map-generator/circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/map-generator/dist/main.js b/map-generator/dist/main.js index cd4fbad..d8633c1 100644 --- a/map-generator/dist/main.js +++ b/map-generator/dist/main.js @@ -16,7 +16,7 @@ \****************/ /***/ ((__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) 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/clzy2ozzf004k01pn840h9xdb',\r\n center: [145.00724,-37.79011]\r\n});\r\n\r\nfetch(\"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 'line-color': '#0899fe',\r\n 'line-width': 3\r\n }\r\n });\r\n\r\n var centrePoint;\r\n if (featureCollection.features[0].geometry.type == \"Polygon\") {\r\n centrePoint = (0,polylabel__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(featureCollection.features[0].geometry.coordinates, 0.000001);\r\n }\r\n if (featureCollection.features[0].geometry.type == \"MultiPolygon\") {\r\n // TODO: Find the biggest polygon in the multipolygon and use that to find the centre point \r\n // instead of just picking the second polygon.\r\n //\r\n // The 2024 set of boundaries only uses 2 MultiPolygon objects (Cathedral in Murrindindi Shire Council and Island in Bass Coast Shire Council)\r\n // Luckily, the second polygon for both objects results in a good label placement.\r\n centrePoint = (0,polylabel__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(featureCollection.features[0].geometry.coordinates[1], 0.000001);\r\n }\r\n \r\n\r\n if (wardData.electorateName.includes(' ')) {\r\n // Breaking long names into newlines looks better\r\n const parts = wardData.electorateName.split(' ');\r\n // Special case if a ward starts with \"St\" (like \"St Albans East\")\r\n // Join the first two parts\r\n if (parts[0] == \"St\") {\r\n parts[0] = parts[0] + ' ' + parts[1];\r\n parts.splice(1, 1);\r\n }\r\n const wardNameNewLines = parts.join('\\n');\r\n labelFeatures.push({\r\n 'type': 'Feature',\r\n 'properties': {\r\n 'description': wardNameNewLines\r\n },\r\n 'geometry': {\r\n 'type': 'Point',\r\n 'coordinates': centrePoint\r\n }\r\n });\r\n } else {\r\n labelFeatures.push({\r\n 'type': 'Feature',\r\n 'properties': {\r\n 'description': wardData.electorateName\r\n },\r\n 'geometry': {\r\n 'type': 'Point',\r\n 'coordinates': centrePoint\r\n }\r\n });\r\n }\r\n });\r\n\r\n map.addSource('labels', {\r\n 'type': 'geojson',\r\n 'data': {\r\n 'type': 'FeatureCollection',\r\n 'features': labelFeatures\r\n }\r\n });\r\n\r\n map.addLayer({\r\n 'id': 'labels',\r\n 'type': 'symbol',\r\n 'source': 'labels',\r\n 'layout': {\r\n 'text-field': ['get', 'description'],\r\n 'text-variable-anchor': ['center', 'top', 'bottom'],\r\n 'text-radial-offset': 0.5,\r\n 'text-padding': 0,\r\n 'text-justify': 'auto',\r\n 'text-allow-overlap': false,\r\n 'text-ignore-placement': false,\r\n }\r\n });\r\n\r\n map.fitBounds([\r\n [bounds.west, bounds.south],\r\n [bounds.east, bounds.north]\r\n ], {\r\n padding: 25,\r\n animate: false\r\n });\r\n\r\n }).catch(err => {\r\n console.log(err);\r\n });\r\n });\r\n \n\n//# sourceURL=webpack:///./src.js?"); +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) 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 'line-color': '#0899fe',\r\n 'line-width': 3\r\n }\r\n });\r\n\r\n var centrePoint;\r\n if (featureCollection.features[0].geometry.type == \"Polygon\") {\r\n centrePoint = (0,polylabel__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(featureCollection.features[0].geometry.coordinates, 0.000001);\r\n }\r\n if (featureCollection.features[0].geometry.type == \"MultiPolygon\") {\r\n // TODO: Find the biggest polygon in the multipolygon and use that to find the centre point \r\n // instead of just picking the second polygon.\r\n //\r\n // The 2024 set of boundaries only uses 2 MultiPolygon objects (Cathedral in Murrindindi Shire Council and Island in Bass Coast Shire Council)\r\n // Luckily, the second polygon for both objects results in a good label placement.\r\n centrePoint = (0,polylabel__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(featureCollection.features[0].geometry.coordinates[1], 0.000001);\r\n }\r\n \r\n\r\n if (wardData.electorateName.includes(' ')) {\r\n // Breaking long names into newlines looks better\r\n const parts = wardData.electorateName.split(' ');\r\n // Special case if a ward starts with \"St\" (like \"St Albans East\")\r\n // Join the first two parts\r\n if (parts[0] == \"St\") {\r\n parts[0] = parts[0] + ' ' + parts[1];\r\n parts.splice(1, 1);\r\n }\r\n const wardNameNewLines = parts.join('\\n');\r\n labelFeatures.push({\r\n 'type': 'Feature',\r\n 'properties': {\r\n 'description': wardNameNewLines\r\n },\r\n 'geometry': {\r\n 'type': 'Point',\r\n 'coordinates': centrePoint\r\n }\r\n });\r\n } else {\r\n labelFeatures.push({\r\n 'type': 'Feature',\r\n 'properties': {\r\n 'description': wardData.electorateName\r\n },\r\n 'geometry': {\r\n 'type': 'Point',\r\n 'coordinates': centrePoint\r\n }\r\n });\r\n }\r\n });\r\n\r\n map.addSource('labels', {\r\n 'type': 'geojson',\r\n 'data': {\r\n 'type': 'FeatureCollection',\r\n 'features': labelFeatures\r\n }\r\n });\r\n\r\n map.addLayer({\r\n 'id': 'labels',\r\n 'type': 'symbol',\r\n 'source': 'labels',\r\n 'layout': {\r\n 'text-field': ['get', 'description'],\r\n 'text-variable-anchor': ['top', 'left', 'bottom', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],\r\n 'text-radial-offset': 1,\r\n 'text-padding': 0,\r\n 'text-justify': 'auto',\r\n 'text-allow-overlap': false,\r\n 'text-ignore-placement': false,\r\n 'icon-image': 'blue-circle'\r\n }\r\n });\r\n\r\n map.fitBounds([\r\n [bounds.west, bounds.south],\r\n [bounds.east, bounds.north]\r\n ], {\r\n padding: 25,\r\n animate: false\r\n });\r\n\r\n }).catch(err => {\r\n console.log(err);\r\n });\r\n });\r\n \r\n });\r\n});\n\n//# sourceURL=webpack:///./src.js?"); /***/ }), diff --git a/map-generator/src.js b/map-generator/src.js index 9f5b2e1..d21e5c9 100644 --- a/map-generator/src.js +++ b/map-generator/src.js @@ -21,166 +21,176 @@ mapboxgl.accessToken = 'pk.eyJ1IjoibWF0dHl3YXkiLCJhIjoiY2x6eG9vMzZyMHY2cDJqb3M1O const map = new mapboxgl.Map({ container: 'map', zoom: 10, - style: 'mapbox://styles/mattyway/clzy2ozzf004k01pn840h9xdb', + style: 'mapbox://styles/mattyway/cm03vw57q00fd01pn93wf2j7p', center: [145.00724,-37.79011] }); -fetch("wards_withboundaries.json") - .then(response => { - response.json() - .then((wardData) => { - const filteredWardData = wardData.filter((ward) => normaliseCouncilName(ward.parentElectorateName) == councilName); +map.on('load', () => { + // Load an image from an external URL. + map.loadImage('circle.png', (error, image) => { + if (error) throw error; - var bounds = { - "west": undefined, - "south": undefined, - "east": undefined, - "north": undefined - } + map.addImage('blue-circle', image); - function addToBounds(coordinate) { - if (bounds.west == undefined || coordinate[0] < bounds.west) { - bounds.west = coordinate[0]; - } + fetch("wards_withboundaries.json") + .then(response => { + response.json() + .then((wardData) => { + const filteredWardData = wardData.filter((ward) => normaliseCouncilName(ward.parentElectorateName) == councilName); - if (bounds.south == undefined || coordinate[1] < bounds.south) { - bounds.south = coordinate[1]; - } + var bounds = { + "west": undefined, + "south": undefined, + "east": undefined, + "north": undefined + } - if (bounds.east == undefined || coordinate[0] > bounds.east) { - bounds.east = coordinate[0]; - } - - if (bounds.north == undefined || coordinate[1] > bounds.north) { - bounds.north = coordinate[1]; - } - } - - var labelFeatures = []; - - filteredWardData.forEach(wardData => { - const featureCollection = { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': JSON.parse(wardData.boundaryJson) + function addToBounds(coordinate) { + if (bounds.west == undefined || coordinate[0] < bounds.west) { + bounds.west = coordinate[0]; } - ] - }; - if (featureCollection.features[0].geometry.type == "Polygon") { - featureCollection.features[0].geometry.coordinates[0].forEach(coordinate => { - addToBounds(coordinate); - }); - } - if (featureCollection.features[0].geometry.type == "MultiPolygon") { - featureCollection.features[0].geometry.coordinates.forEach(polygon => { - polygon[0].forEach(coordinate => { - addToBounds(coordinate); + if (bounds.south == undefined || coordinate[1] < bounds.south) { + bounds.south = coordinate[1]; + } + + if (bounds.east == undefined || coordinate[0] > bounds.east) { + bounds.east = coordinate[0]; + } + + if (bounds.north == undefined || coordinate[1] > bounds.north) { + bounds.north = coordinate[1]; + } + } + + var labelFeatures = []; + + filteredWardData.forEach(wardData => { + const featureCollection = { + 'type': 'FeatureCollection', + 'features': [ + { + 'type': 'Feature', + 'geometry': JSON.parse(wardData.boundaryJson) + } + ] + }; + + if (featureCollection.features[0].geometry.type == "Polygon") { + featureCollection.features[0].geometry.coordinates[0].forEach(coordinate => { + addToBounds(coordinate); + }); + } + if (featureCollection.features[0].geometry.type == "MultiPolygon") { + featureCollection.features[0].geometry.coordinates.forEach(polygon => { + polygon[0].forEach(coordinate => { + addToBounds(coordinate); + }); + }); + } + + // Add data + map.addSource("data_"+wardData.electorateId, { + 'type': 'geojson', + 'data': featureCollection }); - }); - } - // Add data - map.addSource("data_"+wardData.electorateId, { - 'type': 'geojson', - 'data': featureCollection - }); + // Add a line along the data + map.addLayer({ + 'id': "outline_"+wardData.electorateId, + 'type': 'line', + 'source': "data_"+wardData.electorateId, + 'layout': {}, + 'paint': { + 'line-color': '#0899fe', + 'line-width': 3 + } + }); - // Add a line along the data - map.addLayer({ - 'id': "outline_"+wardData.electorateId, - 'type': 'line', - 'source': "data_"+wardData.electorateId, - 'layout': {}, - 'paint': { - 'line-color': '#0899fe', - 'line-width': 3 - } - }); + var centrePoint; + if (featureCollection.features[0].geometry.type == "Polygon") { + centrePoint = polylabel(featureCollection.features[0].geometry.coordinates, 0.000001); + } + if (featureCollection.features[0].geometry.type == "MultiPolygon") { + // TODO: Find the biggest polygon in the multipolygon and use that to find the centre point + // instead of just picking the second polygon. + // + // 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. + centrePoint = polylabel(featureCollection.features[0].geometry.coordinates[1], 0.000001); + } + - var centrePoint; - if (featureCollection.features[0].geometry.type == "Polygon") { - centrePoint = polylabel(featureCollection.features[0].geometry.coordinates, 0.000001); - } - if (featureCollection.features[0].geometry.type == "MultiPolygon") { - // TODO: Find the biggest polygon in the multipolygon and use that to find the centre point - // instead of just picking the second polygon. - // - // 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. - centrePoint = polylabel(featureCollection.features[0].geometry.coordinates[1], 0.000001); - } - - - if (wardData.electorateName.includes(' ')) { - // Breaking long names into newlines looks better - const parts = wardData.electorateName.split(' '); - // Special case if a ward starts with "St" (like "St Albans East") - // Join the first two parts - if (parts[0] == "St") { - parts[0] = parts[0] + ' ' + parts[1]; - parts.splice(1, 1); - } - const wardNameNewLines = parts.join('\n'); - labelFeatures.push({ - 'type': 'Feature', - 'properties': { - 'description': wardNameNewLines - }, - 'geometry': { - 'type': 'Point', - 'coordinates': centrePoint + if (wardData.electorateName.includes(' ')) { + // Breaking long names into newlines looks better + const parts = wardData.electorateName.split(' '); + // Special case if a ward starts with "St" (like "St Albans East") + // Join the first two parts + if (parts[0] == "St") { + parts[0] = parts[0] + ' ' + parts[1]; + parts.splice(1, 1); + } + const wardNameNewLines = parts.join('\n'); + labelFeatures.push({ + 'type': 'Feature', + 'properties': { + 'description': wardNameNewLines + }, + 'geometry': { + 'type': 'Point', + 'coordinates': centrePoint + } + }); + } else { + labelFeatures.push({ + 'type': 'Feature', + 'properties': { + 'description': wardData.electorateName + }, + 'geometry': { + 'type': 'Point', + 'coordinates': centrePoint + } + }); } }); - } else { - labelFeatures.push({ - 'type': 'Feature', - 'properties': { - 'description': wardData.electorateName - }, - 'geometry': { - 'type': 'Point', - 'coordinates': centrePoint + + map.addSource('labels', { + 'type': 'geojson', + 'data': { + 'type': 'FeatureCollection', + 'features': labelFeatures } }); - } - }); - map.addSource('labels', { - 'type': 'geojson', - 'data': { - 'type': 'FeatureCollection', - 'features': labelFeatures - } - }); + map.addLayer({ + 'id': 'labels', + 'type': 'symbol', + 'source': 'labels', + 'layout': { + 'text-field': ['get', 'description'], + 'text-variable-anchor': ['top', 'left', 'bottom', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'], + 'text-radial-offset': 1, + 'text-padding': 0, + 'text-justify': 'auto', + 'text-allow-overlap': false, + 'text-ignore-placement': false, + 'icon-image': 'blue-circle' + } + }); - map.addLayer({ - 'id': 'labels', - 'type': 'symbol', - 'source': 'labels', - 'layout': { - 'text-field': ['get', 'description'], - 'text-variable-anchor': ['center', 'top', 'bottom'], - 'text-radial-offset': 0.5, - 'text-padding': 0, - 'text-justify': 'auto', - 'text-allow-overlap': false, - 'text-ignore-placement': false, - } - }); + map.fitBounds([ + [bounds.west, bounds.south], + [bounds.east, bounds.north] + ], { + padding: 25, + animate: false + }); - map.fitBounds([ - [bounds.west, bounds.south], - [bounds.east, bounds.north] - ], { - padding: 25, - animate: false - }); - - }).catch(err => { - console.log(err); + }).catch(err => { + console.log(err); + }); }); - }); - \ No newline at end of file + + }); +}); \ No newline at end of file