Kā uztaisīt labāku novadu karti
Jo LSM karte bija diezgan sāpīga
Iespējams, ka šovasar pamanījāt šo karti no LSM ar pašvaldību vēlēšanu rezultātiem, kur katrs novads izkrāsots tā saraksta krāsā, kurš saņēmis visvairāk balsu.
Šī karte mājaslapas ietvaros nav nemaz tik slikta. Uzbraucot uz katra novada, ir iespējams redzēt sarakstus un iegūtās balsis. Un, uz novada nospiežot, var redzēt plašāku informāciju, piemēram, sarakstu iegūtos mandātus.
Taču, tīri kā attēls, šī karte ir pavisam baisa. Lūk, galvenie noziegumi īsumā:
- Ir ļoti daudz zaļo, zilo un tumši sarkano toņu, kurus dažreiz ir gandrīz neiespējami atšķirt savā starpā.
Olainē, Rīgā un Ādažos ir uzvarējuši trīs dažādi politiskie spēki.
- Starp Madonas novadu un bijušo Varakļānu novadu vēl ir attēlota robeža.
- Gan novadu robežas, gan fons, gan novadu nosaukumi ir balti, tādēļ daudzi nosaukumi ir nesalasāmi.
Lai gan, Aizkraukles novada nosaukums ir melnā krāsā.
Ideja
Principā, šīm problēmām ir diezgan vienkāršs risinājums: izmantot tumšu fonu un dažiem novadiem piešķirt strīpainus krāsojumus. Lūk, šāda eksperimenta rezultāts:
Ja lieto telefonu, pagriez to horizontāli.
Tagad ir skaidrs, ka Olainē, Rīgā un Ādažos tiešām uzvarējuši trīs dažādi politiskie spēki.
Iepazīsimies ar to, kā šādu karti izveidot ar Python un Javascript/Leaflet.
Python
Pirmkārt, vajadzīgs izveidot failu, kas satur novadus kā daudzstūrus, uzvarētājus un partiju krāsas. Šim nolūkam var izmantot Python ar geopandas
bibliotēku.
Vispirms lejupielādējam datu kopumu, ko izveidojis Valsts zemes dienests, un saglabājam divus failus no tā, kas ir Novadi.shp
un Pilsetas.shp
.
Fails
Pilsetas.shp
satur tabulu ar visām Latvijas pilsētām. Mēs vēlamies saglabāt tikai valstspilsētas; tām kolonnāCITY_TYPE
piešķirta vērtība2
.
Ar šiem failiem varam izveidot tabulu, kas satur visu novadu nosaukumus un figūras.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import geopandas as gpd, pandas as pd
nov = gpd.read_file("Novadi.shp")
pil = gpd.read_file("Pilsetas.shp")
# Apvienojam Madonu un Varakļānus
nov.loc[nov["LABEL"] == "Varakļānu nov.", "LABEL"] = "Madonas nov."
nov = nov.dissolve(by="LABEL").reset_index()
# Pievienojam valstspilsētas
nov = pd.concat([nov, pil[pil["CITY_TYPE"] == 2]]).reset_index()
# Saglabājam tikai nosaukumus un figūras
nov = nov[["LABEL", "geometry"]]
Tālāk, mēs vēlamies pievienot rezultātus. Šim nolūkam es izmantoju Google Sheets, lai manuāli izveidotu tabulu winners.tsv
ar novada nosaukumu, attiecīgo vēlēšanu uzvarētāju un divām krāsām.
Mēs apvienojam esošo tabulu nov
ar šo uzvarētāju tabulu, un izvadām rezultātus GeoJSON
formātā. Mēs beigās arī pārejam uz EPSG:4326
koordinātu sistēmu, ko sagaida Leaflet.
1
2
3
4
5
6
7
8
# Izlasīt uzvarētāju tabulu
win = pd.read_csv("winners.tsv", sep='\t')
# Apvienot novadu nosaukumus un figūras ar uzvarētājiem
res = nov.merge(win, on="LABEL")
# Izveidot GeoJSON failu izmantojot beigās iegūto tabulu
res.to_crs(epsg=4326).to_file("election_data.geojson", driver="GeoJSON")
Javascript / Leaflet
Mūsu GeoJSON
failā esošos datos var attēlot, piemēram, ar Leaflet. Jāatzīst, ka attiecīgais kods ir diezgan garš, tāpēc šeit ir tikai galveno domu apkopojums. Pilnu kodu var skatīt GitHub.
Vispirms, mēs varam izveidot primitīvu HTML lapu. Priekš Leaflet izmantošanas pievienojam bibliotēku un tās stylesheet
, kā arī leaflet.pattern.js
, ko var lejupielādēt no GitHub. Nolasām mūsu GeoJSON
failu un saglabājam tā saturu kā data
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<style>
/* CSS stila norādījumi jāievieto šeit */
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="leaflet.pattern.js"></script>
<script>
fetch('election_data.geojson').then(response => response.json()).then(data => {
/* JavaScript kods jāievieto šeit */
});
</script>
</body>
</html>
Ar JavaScript vispirms izveidojam const map = L.map('map', { /* ... */ })
ar statisku karti, un tad pievienojam failā esošo informāciju apmēram šādi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Izveidojam slāni ar failā esošo informāciju
const geojsonLayer = L.geoJSON(data, {
onEachFeature: function (feature, layer) {
// Pievienojam novadu otrajā krāsā
L.geoJSON(feature, {
style: {
fillColor: feature.properties.secondaryColor,
/* ... */
}
}).addTo(map);
// Uzliekam biezas strīpas pirmajā krāsā
const pattern = new L.StripePattern({
color: feature.properties.mainColor,
/* ... */
});
pattern.addTo(map);
layer.setStyle({
fillPattern: pattern,
/* ... */
});
// Un izveidojam tooltip, kas parādās, uzbraucot uz novada
layer.bindTooltip(/* ... */);
}
});
// Pievienojam slāni kartei
geojsonLayer.addTo(map);
// Tālummaiņa
map.fitBounds(geojsonLayer.getBounds(), { padding: [20, 20] });
map.touchZoom.disable();
// Te var pievienot kodu, kas atkārto tālummaiņu, mainoties kartes izmēram
Kad karte ir izveidojusies, mēs visbeidzot varam pievienot novadu nosaukumus. Diemžēl, Leaflet nav laba metode, kas garantē visu nosaukumu redzamību, bet kaut ko līdzīgu var panākt manuāli.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
map.whenReady(() => {
geojsonLayer.eachLayer(layer => {
const label = layer.feature.properties.LABEL;
const center = layer.getBounds().getCenter();
// Iepriekš var izveidot vārdnīcu - kā jāpabīda nosaukumi, lai tie nepārklātos
const offset = labelOffsets[label] || [0, 0];
const offsetLatLng = {"lat": center["lat"] + offset[1], "lng": center["lng"] + offset[0]};
// Pievienojam nosaukumu
L.marker(offsetLatLng, {
icon: L.divIcon({
html: label,
/* ... */
}),
interactive: false
}).addTo(map);
});
});
Atliek pievienot stila norādes, un jaunā karte ir gatava!
Iespējamie uzlabojumi
Ir dažādi veidi, kā šo karti var uzlabot. Ir divas lietas, ko LSM jau ir izdarījuši:
- Pievienot vairāk datu. Šis varētu būt vieglāk, ja un kad mājaslapā data.gov.lv parādīsies vēlēšanu rezultātu datu kopums.
- Pataisīt karti interaktīvāku, piemēram, novirzot lietotāju uz plašākiem rezultātiem, piespiežot uz novada.
Taču papildus tam:
- Atrast efektīvāku veidu vai stratēģiju, kā atzīmēt novadu nosaukumus.
- Izvēlēties izskatīgākus krāsojumus, kā arī tādus, ko būtu vieglāk atšķirt cilvēkiem ar krāsu redzes traucējumiem.
- Apsvērt citu kartes stilu! Eksistē dažādi interesanti veidi, kā attēlot vēlēšanu rezultātus. Viens piemērs šeit.
Katrā gadījumā, šeit jau veiktās vizuālās pārmaiņas ir diezgan vienkāršas. Cerams, ka LSM kādreiz tādas īstenos.