From 1f4ee22f77cee16e1c4a8235cacd26606888a7d2 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 14:27:36 +0000 Subject: [PATCH 01/30] Add Settlement_hierarchy model to ShapesTest and update assertions --- seshat/apps/core/tests/tests.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/seshat/apps/core/tests/tests.py b/seshat/apps/core/tests/tests.py index deeece1bc..637129206 100644 --- a/seshat/apps/core/tests/tests.py +++ b/seshat/apps/core/tests/tests.py @@ -4,7 +4,7 @@ from django.urls import reverse from ..models import Cliopatria, GADMShapefile, GADMCountries, GADMProvinces, Polity, Capital from ...general.models import Polity_capital, Polity_peak_years, Polity_language, Polity_religious_tradition -from ...sc.models import Judge +from ...sc.models import Judge, Settlement_hierarchy from ...wf.models import Copper from ...rt.models import Gov_res_pub_pros from ..views import get_provinces, get_polity_shape_content, get_all_polity_capitals, assign_variables_to_shapes, assign_categorical_variables_to_shapes @@ -176,6 +176,12 @@ def setUp(self): religious_tradition='Islam', polity_id=2 ) + Settlement_hierarchy.objects.create( + name='settlement_hierarchy', + settlement_hierarchy_from=6, + settlement_hierarchy_to=7, + polity_id=2 + ) # Model tests @@ -586,4 +592,6 @@ def test_assign_categorical_variables_to_shapes(self): self.assertEqual(result_shapes[0]['language'], ['English', 'French']) self.assertEqual(result_shapes[0]['language_dict']['English'], [1998, 2000]) self.assertEqual(result_shapes[0]['language_dict']['French'], [1999, 2007]) - self.assertEqual(result_shapes[0]['religious_tradition'], ['Christianity', 'Islam']) \ No newline at end of file + self.assertEqual(result_shapes[0]['religious_tradition'], ['Christianity', 'Islam']) + self.assertEqual(result_shapes[0]['settlement_hierarchy_from'], 6) + self.assertEqual(result_shapes[0]['settlement_hierarchy_to'], 7) \ No newline at end of file From 26ea9e6c61d11dbd6f8d0e9719c6665587bb9f22 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 14:45:11 +0000 Subject: [PATCH 02/30] Add settlement hierarchy variables to shapes --- seshat/apps/core/views.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index 8a734b158..9a2f119d1 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -69,6 +69,7 @@ from django.contrib.messages.views import SuccessMessageMixin from ..general.models import Polity_research_assistant, Polity_duration, Polity_linguistic_family, Polity_language_genus, Polity_language, POLITY_LINGUISTIC_FAMILY_CHOICES, POLITY_LANGUAGE_GENUS_CHOICES, POLITY_LANGUAGE_CHOICES, Polity_religious_tradition, Polity_religion_genus, Polity_religion_family, Polity_religion, Polity_alternate_religion_genus, Polity_alternate_religion_family, Polity_alternate_religion, POLITY_RELIGION_GENUS_CHOICES, POLITY_RELIGION_FAMILY_CHOICES, POLITY_RELIGION_CHOICES +from ..sc.models import Settlement_hierarchy from ..crisisdb.models import Power_transition @@ -4253,7 +4254,7 @@ def assign_categorical_variables_to_shapes(shapes, variables): Assign the categorical variables to the shapes. Note: - Currently only language and religion variables are implemented. + Extend this function to add more of the variables. Args: shapes (list): The shapes to assign the variables to. @@ -4262,7 +4263,7 @@ def assign_categorical_variables_to_shapes(shapes, variables): Returns: tuple: A tuple containing the shapes and the variables. """ - # Add categorical variables to the variables dictionary + # Add categorical variables from General Variables to the variables dictionary variables['General Variables'] = { 'polity_linguistic_family': {'formatted': 'linguistic_family', 'full_name': 'Linguistic Family'}, 'polity_language_genus': {'formatted': 'language_genus', 'full_name': 'Language Genus'}, @@ -4276,10 +4277,16 @@ def assign_categorical_variables_to_shapes(shapes, variables): 'polity_alternate_religion': {'formatted': 'alternate_religion', 'full_name': 'Alternate Religion'}, } + # Add categorical variables from Social Complexity Variables to the variables dictionary + if 'Social Complexity Variables' not in variables: + variables['Social Complexity Variables'] = {} + variables['Social Complexity Variables']['settlement_hierarchy'] = {'formatted': 'settlement_hierarchy', 'full_name': 'Settlement Hierarchy'} + + # Fetch all polities and store them in a dictionary for quick access polities = {polity.new_name: polity for polity in Polity.objects.all()} - # Fetch all linguistic families, language genuses, and languages and store them in dictionaries for quick access + # Fetch all categorical variables and store them in dictionaries for quick access linguistic_families = {} for lf in Polity_linguistic_family.objects.all(): if lf.polity_id not in linguistic_families: @@ -4340,7 +4347,7 @@ def assign_categorical_variables_to_shapes(shapes, variables): alternate_religions[ar.polity_id] = [] alternate_religions[ar.polity_id].append(ar) - # Add language variable info to polity shapes + # Add categorical variable info to polity shapes for shape in shapes: shape['linguistic_family'] = [] shape['linguistic_family_dict'] = {} @@ -4362,6 +4369,8 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_family_dict'] = {} shape['alternate_religion'] = [] shape['alternate_religion_dict'] = {} + shape['settlement_hierarchy_from'] = 0 + shape['settlement_hierarchy_to'] = 0 if shape['seshat_id'] != 'none': # Skip shapes with no seshat_id polity = polities.get(shape['seshat_id']) if polity: @@ -4376,8 +4385,10 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_genus'].extend([arg.alternate_religion_genus for arg in alternate_religion_genuses.get(polity.id, [])]) shape['alternate_religion_family'].extend([arf.alternate_religion_family for arf in alternate_religion_families.get(polity.id, [])]) shape['alternate_religion'].extend([ar.alternate_religion for ar in alternate_religions.get(polity.id, [])]) + shape['settlement_hierarchy_from'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_from + shape['settlement_hierarchy_to'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_to - # Get the years for the variables for the polity + # Get the years for the variables which have years for the polity shape['linguistic_family_dict'].update({lf.linguistic_family: [lf.year_from, lf.year_to] for lf in linguistic_families.get(polity.id, [])}) shape['language_genus_dict'].update({lg.language_genus: [lg.year_from, lg.year_to] for lg in language_genuses.get(polity.id, [])}) shape['language_dict'].update({l.language: [l.year_from, l.year_to] for l in languages.get(polity.id, [])}) @@ -4389,7 +4400,7 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_family_dict'].update({arf.alternate_religion_family: [arf.year_from, arf.year_to] for arf in alternate_religion_families.get(polity.id, [])}) shape['alternate_religion_dict'].update({ar.alternate_religion: [ar.year_from, ar.year_to] for ar in alternate_religions.get(polity.id, [])}) - # If no linguistic family, language genus, or language was found, append 'Uncoded' + # If no variable was found, append 'Uncoded' polity = polities.get(shape['seshat_id']) if polity: if not shape['linguistic_family']: From 0bb4676f2a824f9698206c49537435792fd773bc Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 14:50:24 +0000 Subject: [PATCH 03/30] Add religious, military, and administrative level models to test_assign_categorical_variables_to_shapes --- seshat/apps/core/tests/tests.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/seshat/apps/core/tests/tests.py b/seshat/apps/core/tests/tests.py index 637129206..88fd3ef94 100644 --- a/seshat/apps/core/tests/tests.py +++ b/seshat/apps/core/tests/tests.py @@ -4,7 +4,7 @@ from django.urls import reverse from ..models import Cliopatria, GADMShapefile, GADMCountries, GADMProvinces, Polity, Capital from ...general.models import Polity_capital, Polity_peak_years, Polity_language, Polity_religious_tradition -from ...sc.models import Judge, Settlement_hierarchy +from ...sc.models import Judge, Settlement_hierarchy, Religious_level, Military_level, Administrative_level from ...wf.models import Copper from ...rt.models import Gov_res_pub_pros from ..views import get_provinces, get_polity_shape_content, get_all_polity_capitals, assign_variables_to_shapes, assign_categorical_variables_to_shapes @@ -182,6 +182,24 @@ def setUp(self): settlement_hierarchy_to=7, polity_id=2 ) + Religious_level.objects.create( + name='religious_level', + religious_level_from=9, + religious_level_to=10, + polity_id=2 + ) + Military_level.objects.create( + name='military_level', + military_level_from=13, + military_level_to=14, + polity_id=2 + ) + Administrative_level.objects.create( + name='administrative_level', + administrative_level_from=5, + administrative_level_to=6, + polity_id=2 + ) # Model tests @@ -594,4 +612,10 @@ def test_assign_categorical_variables_to_shapes(self): self.assertEqual(result_shapes[0]['language_dict']['French'], [1999, 2007]) self.assertEqual(result_shapes[0]['religious_tradition'], ['Christianity', 'Islam']) self.assertEqual(result_shapes[0]['settlement_hierarchy_from'], 6) - self.assertEqual(result_shapes[0]['settlement_hierarchy_to'], 7) \ No newline at end of file + self.assertEqual(result_shapes[0]['settlement_hierarchy_to'], 7) + self.assertEqual(result_shapes[0]['religious_level_from'], 9) + self.assertEqual(result_shapes[0]['religious_level_to'], 10) + self.assertEqual(result_shapes[0]['military_level_from'], 13) + self.assertEqual(result_shapes[0]['military_level_to'], 14) + self.assertEqual(result_shapes[0]['administrative_level_from'], 5) + self.assertEqual(result_shapes[0]['administrative_level_to'], 6) \ No newline at end of file From 592817cb9ea746159af5a3a4811924b4a643154f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 14:54:53 +0000 Subject: [PATCH 04/30] Add religious, military, and administrative level variables to assign_categorical_variables_to_shapes --- seshat/apps/core/views.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index 9a2f119d1..62279627a 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -69,7 +69,7 @@ from django.contrib.messages.views import SuccessMessageMixin from ..general.models import Polity_research_assistant, Polity_duration, Polity_linguistic_family, Polity_language_genus, Polity_language, POLITY_LINGUISTIC_FAMILY_CHOICES, POLITY_LANGUAGE_GENUS_CHOICES, POLITY_LANGUAGE_CHOICES, Polity_religious_tradition, Polity_religion_genus, Polity_religion_family, Polity_religion, Polity_alternate_religion_genus, Polity_alternate_religion_family, Polity_alternate_religion, POLITY_RELIGION_GENUS_CHOICES, POLITY_RELIGION_FAMILY_CHOICES, POLITY_RELIGION_CHOICES -from ..sc.models import Settlement_hierarchy +from ..sc.models import Settlement_hierarchy, Religious_level, Military_level, Administrative_level from ..crisisdb.models import Power_transition @@ -4281,7 +4281,9 @@ def assign_categorical_variables_to_shapes(shapes, variables): if 'Social Complexity Variables' not in variables: variables['Social Complexity Variables'] = {} variables['Social Complexity Variables']['settlement_hierarchy'] = {'formatted': 'settlement_hierarchy', 'full_name': 'Settlement Hierarchy'} - + variables['Social Complexity Variables']['religious_level'] = {'formatted': 'religious_level', 'full_name': 'Religious Level'} + variables['Social Complexity Variables']['military_level'] = {'formatted': 'military_level', 'full_name': 'Military Level'} + variables['Social Complexity Variables']['administrative_level'] = {'formatted': 'administrative_level', 'full_name': 'Administrative Level'} # Fetch all polities and store them in a dictionary for quick access polities = {polity.new_name: polity for polity in Polity.objects.all()} @@ -4371,6 +4373,12 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_dict'] = {} shape['settlement_hierarchy_from'] = 0 shape['settlement_hierarchy_to'] = 0 + shape['religious_level_from'] = 0 + shape['religious_level_to'] = 0 + shape['military_level_from'] = 0 + shape['military_level_to'] = 0 + shape['administrative_level_from'] = 0 + shape['administrative_level_to'] = 0 if shape['seshat_id'] != 'none': # Skip shapes with no seshat_id polity = polities.get(shape['seshat_id']) if polity: @@ -4387,6 +4395,12 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion'].extend([ar.alternate_religion for ar in alternate_religions.get(polity.id, [])]) shape['settlement_hierarchy_from'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_from shape['settlement_hierarchy_to'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_to + shape['religious_level_from'] = Religious_level.objects.filter(polity_id=polity.id)[0].religious_level_from + shape['religious_level_to'] = Religious_level.objects.filter(polity_id=polity.id)[0].religious_level_to + shape['military_level_from'] = Military_level.objects.filter(polity_id=polity.id)[0].military_level_from + shape['military_level_to'] = Military_level.objects.filter(polity_id=polity.id)[0].military_level_to + shape['administrative_level_from'] = Administrative_level.objects.filter(polity_id=polity.id)[0].administrative_level_from + shape['administrative_level_to'] = Administrative_level.objects.filter(polity_id=polity.id)[0].administrative_level_to # Get the years for the variables which have years for the polity shape['linguistic_family_dict'].update({lf.linguistic_family: [lf.year_from, lf.year_to] for lf in linguistic_families.get(polity.id, [])}) From 04b748c39a7ba5c6786cbede64af7581e8005e22 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 15:52:48 +0000 Subject: [PATCH 05/30] Refactor assign_categorical_variables_to_shapes to handle empty queryset cases for settlement, religious, military, and administrative levels --- seshat/apps/core/views.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index 62279627a..0ef69b273 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -4393,14 +4393,22 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_genus'].extend([arg.alternate_religion_genus for arg in alternate_religion_genuses.get(polity.id, [])]) shape['alternate_religion_family'].extend([arf.alternate_religion_family for arf in alternate_religion_families.get(polity.id, [])]) shape['alternate_religion'].extend([ar.alternate_religion for ar in alternate_religions.get(polity.id, [])]) - shape['settlement_hierarchy_from'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_from - shape['settlement_hierarchy_to'] = Settlement_hierarchy.objects.filter(polity_id=polity.id)[0].settlement_hierarchy_to - shape['religious_level_from'] = Religious_level.objects.filter(polity_id=polity.id)[0].religious_level_from - shape['religious_level_to'] = Religious_level.objects.filter(polity_id=polity.id)[0].religious_level_to - shape['military_level_from'] = Military_level.objects.filter(polity_id=polity.id)[0].military_level_from - shape['military_level_to'] = Military_level.objects.filter(polity_id=polity.id)[0].military_level_to - shape['administrative_level_from'] = Administrative_level.objects.filter(polity_id=polity.id)[0].administrative_level_from - shape['administrative_level_to'] = Administrative_level.objects.filter(polity_id=polity.id)[0].administrative_level_to + settlement_hierarchy = Settlement_hierarchy.objects.filter(polity_id=polity.id) + if settlement_hierarchy: + shape['settlement_hierarchy_from'] = settlement_hierarchy[0].settlement_hierarchy_from + shape['settlement_hierarchy_to'] = settlement_hierarchy[0].settlement_hierarchy_to + religious_level = Religious_level.objects.filter(polity_id=polity.id) + if religious_level: + shape['religious_level_from'] = religious_level[0].religious_level_from + shape['religious_level_to'] = religious_level[0].religious_level_to + military_level = Military_level.objects.filter(polity_id=polity.id) + if military_level: + shape['military_level_from'] = military_level[0].military_level_from + shape['military_level_to'] = military_level[0].military_level_to + administrative_level = Administrative_level.objects.filter(polity_id=polity.id) + if administrative_level: + shape['administrative_level_from'] = administrative_level[0].administrative_level_from + shape['administrative_level_to'] = administrative_level[0].administrative_level_to # Get the years for the variables which have years for the polity shape['linguistic_family_dict'].update({lf.linguistic_family: [lf.year_from, lf.year_to] for lf in linguistic_families.get(polity.id, [])}) From af3e7901f5810707cab2cd507326c387ef246f47 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 7 Jan 2025 15:57:31 +0000 Subject: [PATCH 06/30] add settlement hierarchy et al to world map - not working --- .../apps/core/templates/core/world_map.html | 187 +++++++++++++----- 1 file changed, 133 insertions(+), 54 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index e951d944a..f2cec2f8d 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -667,6 +667,30 @@

How to use the Seshat World Map

if (shape.alternate_religion) { shape.alternate_religion = JSON.parse(shape.alternate_religion.replace(/'/g, '"')); } + if (shape.settlement_hierarchy_from) { + shape.settlement_hierarchy_from = JSON.parse(shape.settlement_hierarchy.replace(/'/g, '"')); + } + if (shape.settlement_hierarchy_to) { + shape.settlement_hierarchy_to = JSON.parse(shape.settlement_hierarchy.replace(/'/g, '"')); + } + if (shape.religious_level_from) { + shape.religious_level_from = JSON.parse(shape.religious_level_from.replace(/'/g, '"')); + } + if (shape.religious_level_to) { + shape.religious_level_to = JSON.parse(shape.religious_level_to.replace(/'/g, '"')); + } + if (shape.military_level_from) { + shape.military_level_from = JSON.parse(shape.military_level_from.replace(/'/g, '"')); + } + if (shape.military_level_to) { + shape.military_level_to = JSON.parse(shape.military_level_to.replace(/'/g, '"')); + } + if (shape.administrative_level_from) { + shape.administrative_level_from = JSON.parse(shape.administrative_level_from.replace(/'/g, '"')); + } + if (shape.administrative_level_to) { + shape.administrative_level_to = JSON.parse(shape.administrative_level_to.replace(/'/g, '"')); + } // Any fields that end _dict should be converted to a dictionary from a string for (const [key, value] of Object.entries(shape)) { if (key.endsWith('_dict')) { @@ -814,6 +838,24 @@

How to use the Seshat World Map

'No Seshat page': 'silver' }; + let hierarchicalComplexityColourMapping = { + 0: 'grey', + 1: '#ff9c9c', + 2: '#ffc68c', + 3: '#82c1c1', + 4: 'orange', + 5: 'blue', + 6: 'purple', + 7: 'green', + 8: 'red', + 9: 'yellow', + 10: 'brown', + 11: 'pink', + 12: 'cyan', + 13: 'magenta', + 14: 'teal' + }; + function switchBaseMapWorldMap() { switchBaseMap(); if (document.getElementById('chooseVariable').value != 'polity') { @@ -962,6 +1004,11 @@

How to use the Seshat World Map

shapeColour = oneLanguageColourMapping['No Seshat page']; } } + + // Hierarchical complexity variables + } else if (variable == 'settlement_hierarchy_from' || 'settlement_hierarchy_to' || variable == 'religious_level_from' || 'religious_level_to' || variable == 'military_level_from' || 'military_level_to' || variable == 'administrative_level_from' || 'administrative_level_to') { + shapeWeight = polityBorderWeightSelected; + shapeColour = hierarchicalComplexityColourMapping[shape[variable]]; } else { // Absent-present variables shapeWeight = polityBorderWeightSelected; @@ -1158,60 +1205,92 @@

How to use the Seshat World Map

// this will need to be updated to use shape[variable + '_dict'] as done above for absent/present variables // Note: the actual colouring of the shapes is done in the style function above and this is already set up to handle the dictionaries popupContent = popupContent + ` - - Languages - ${shape.language.join(', ')} - - - Language Genus - ${shape.language_genus.join(', ')} - - - Linguistic Family - ${shape.linguistic_family.join(', ')} - - `; - } - if (variable == 'religious_tradition' || variable == 'religion' || variable == 'religion_family' || variable == 'religion_genus' || variable == 'alternate_religion' || variable == 'alternate_religion_family' || variable == 'alternate_religion_genus') { - popupContent = popupContent + ` - - Religious Traditions - ${shape.religious_tradition} - - - Religions - ${shape.religion.join(', ')} - - - Religion Genus - ${shape.religion_genus.join(', ')} - - - Religion Family - ${shape.religion_family.join(', ')} - - - Alternate Religions - ${shape.alternate_religion.join(', ')} - - - Alternate Religion Genus - ${shape.alternate_religion_genus.join(', ')} - - - Alternate Religion Family - ${shape.alternate_religion_family.join(', ')} - - `; - } - if (allCapitalsInfo[shape.seshat_id]) { - if (allCapitalsInfo[shape.seshat_id]['year_from'] <= selectedYearInt && allCapitalsInfo[shape.seshat_id]['year_to'] >= selectedYearInt) { - popupContent = popupContent + ` - - Capital: - ${allCapitalsInfo[shape.seshat_id].map(capital => capital.capital).join(', ')} - - `; + + Languages + ${shape.language.join(', ')} + + + Language Genus + ${shape.language_genus.join(', ')} + + + Linguistic Family + ${shape.linguistic_family.join(', ')} + + `; + } + if (variable == 'religious_tradition' || variable == 'religion' || variable == 'religion_family' || variable == 'religion_genus' || variable == 'alternate_religion' || variable == 'alternate_religion_family' || variable == 'alternate_religion_genus') { + popupContent = popupContent + ` + + Religious Traditions + ${shape.religious_tradition} + + + Religions + ${shape.religion.join(', ')} + + + Religion Genus + ${shape.religion_genus.join(', ')} + + + Religion Family + ${shape.religion_family.join(', ')} + + + Alternate Religions + ${shape.alternate_religion.join(', ')} + + + Alternate Religion Genus + ${shape.alternate_religion_genus.join(', ')} + + + Alternate Religion Family + ${shape.alternate_religion_family.join(', ')} + + `; + } + if (variable == 'settlement_hierarchy_from' || variable == 'settlement_hierarchy_to') { + popupContent = popupContent + ` + + Settlement Hierarchy + ${shape.settlement_hierarchy_from} to ${shape.settlement_hierarchy_to} + + `; + } + if (variable == 'religious_level_from' || variable == 'religious_level_to') { + popupContent = popupContent + ` + + Religious Level + ${shape.religious_level_from} to ${shape.religious_level_to} + + `; + } + if (variable == 'military_level_from' || variable == 'military_level_to') { + popupContent = popupContent + ` + + Military Level + ${shape.military_level_from} to ${shape.military_level_to} + + `; + } + if (variable == 'administrative_level_from' || variable == 'administrative_level_to') { + popupContent = popupContent + ` + + Administrative Level + ${shape.administrative_level_from} to ${shape.administrative_level_to} + + `; + } + if (allCapitalsInfo[shape.seshat_id]) { + if (allCapitalsInfo[shape.seshat_id]['year_from'] <= selectedYearInt && allCapitalsInfo[shape.seshat_id]['year_to'] >= selectedYearInt) { + popupContent = popupContent + ` + + Capital: + ${allCapitalsInfo[shape.seshat_id].map(capital => capital.capital).join(', ')} + + `; popupContent = popupContent + ` From ba21ad3d7d7ff69ca99e2e82a97f969f3eefd6b1 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 10:55:37 +0000 Subject: [PATCH 07/30] Refactor shape variable assignments to use arrays for settlement, religious, military, and administrative levels --- seshat/apps/core/tests/tests.py | 12 ++++-------- seshat/apps/core/views.py | 28 ++++++++++++---------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/seshat/apps/core/tests/tests.py b/seshat/apps/core/tests/tests.py index 88fd3ef94..3e57586c4 100644 --- a/seshat/apps/core/tests/tests.py +++ b/seshat/apps/core/tests/tests.py @@ -611,11 +611,7 @@ def test_assign_categorical_variables_to_shapes(self): self.assertEqual(result_shapes[0]['language_dict']['English'], [1998, 2000]) self.assertEqual(result_shapes[0]['language_dict']['French'], [1999, 2007]) self.assertEqual(result_shapes[0]['religious_tradition'], ['Christianity', 'Islam']) - self.assertEqual(result_shapes[0]['settlement_hierarchy_from'], 6) - self.assertEqual(result_shapes[0]['settlement_hierarchy_to'], 7) - self.assertEqual(result_shapes[0]['religious_level_from'], 9) - self.assertEqual(result_shapes[0]['religious_level_to'], 10) - self.assertEqual(result_shapes[0]['military_level_from'], 13) - self.assertEqual(result_shapes[0]['military_level_to'], 14) - self.assertEqual(result_shapes[0]['administrative_level_from'], 5) - self.assertEqual(result_shapes[0]['administrative_level_to'], 6) \ No newline at end of file + self.assertEqual(result_shapes[0]['settlement_hierarchy'], [6, 7]) + self.assertEqual(result_shapes[0]['religious_level'], [9, 10]) + self.assertEqual(result_shapes[0]['military_level'], [13, 14]) + self.assertEqual(result_shapes[0]['administrative_level'], [5, 6]) \ No newline at end of file diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index 0ef69b273..f40a085cc 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -4371,14 +4371,10 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion_family_dict'] = {} shape['alternate_religion'] = [] shape['alternate_religion_dict'] = {} - shape['settlement_hierarchy_from'] = 0 - shape['settlement_hierarchy_to'] = 0 - shape['religious_level_from'] = 0 - shape['religious_level_to'] = 0 - shape['military_level_from'] = 0 - shape['military_level_to'] = 0 - shape['administrative_level_from'] = 0 - shape['administrative_level_to'] = 0 + shape['settlement_hierarchy'] = [] + shape['religious_level'] = [] + shape['military_level'] = [] + shape['administrative_level'] = [] if shape['seshat_id'] != 'none': # Skip shapes with no seshat_id polity = polities.get(shape['seshat_id']) if polity: @@ -4395,20 +4391,20 @@ def assign_categorical_variables_to_shapes(shapes, variables): shape['alternate_religion'].extend([ar.alternate_religion for ar in alternate_religions.get(polity.id, [])]) settlement_hierarchy = Settlement_hierarchy.objects.filter(polity_id=polity.id) if settlement_hierarchy: - shape['settlement_hierarchy_from'] = settlement_hierarchy[0].settlement_hierarchy_from - shape['settlement_hierarchy_to'] = settlement_hierarchy[0].settlement_hierarchy_to + shape['settlement_hierarchy'].append(settlement_hierarchy[0].settlement_hierarchy_from) + shape['settlement_hierarchy'].append(settlement_hierarchy[0].settlement_hierarchy_to) religious_level = Religious_level.objects.filter(polity_id=polity.id) if religious_level: - shape['religious_level_from'] = religious_level[0].religious_level_from - shape['religious_level_to'] = religious_level[0].religious_level_to + shape['religious_level'].append(religious_level[0].religious_level_from) + shape['religious_level'].append(religious_level[0].religious_level_to) military_level = Military_level.objects.filter(polity_id=polity.id) if military_level: - shape['military_level_from'] = military_level[0].military_level_from - shape['military_level_to'] = military_level[0].military_level_to + shape['military_level'].append(military_level[0].military_level_from) + shape['military_level'].append(military_level[0].military_level_to) administrative_level = Administrative_level.objects.filter(polity_id=polity.id) if administrative_level: - shape['administrative_level_from'] = administrative_level[0].administrative_level_from - shape['administrative_level_to'] = administrative_level[0].administrative_level_to + shape['administrative_level'].append(administrative_level[0].administrative_level_from) + shape['administrative_level'].append(administrative_level[0].administrative_level_to) # Get the years for the variables which have years for the polity shape['linguistic_family_dict'].update({lf.linguistic_family: [lf.year_from, lf.year_to] for lf in linguistic_families.get(polity.id, [])}) From 3b51ec7f9efd3b9b72133d10878e0067d476d0bc Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 11:06:10 +0000 Subject: [PATCH 08/30] Refactor world map template to use arrays for settlement, religious, military, and administrative levels --- .../apps/core/templates/core/world_map.html | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index f2cec2f8d..07258b548 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -667,29 +667,17 @@

How to use the Seshat World Map

if (shape.alternate_religion) { shape.alternate_religion = JSON.parse(shape.alternate_religion.replace(/'/g, '"')); } - if (shape.settlement_hierarchy_from) { - shape.settlement_hierarchy_from = JSON.parse(shape.settlement_hierarchy.replace(/'/g, '"')); + if (shape.settlement_hierarchy) { + shape.settlement_hierarchy = JSON.parse(shape.settlement_hierarchy.replace(/'/g, '"')); } - if (shape.settlement_hierarchy_to) { - shape.settlement_hierarchy_to = JSON.parse(shape.settlement_hierarchy.replace(/'/g, '"')); + if (shape.religious_level) { + shape.religious_level = JSON.parse(shape.religious_level.replace(/'/g, '"')); } - if (shape.religious_level_from) { - shape.religious_level_from = JSON.parse(shape.religious_level_from.replace(/'/g, '"')); + if (shape.military_level) { + shape.military_level = JSON.parse(shape.military_level.replace(/'/g, '"')); } - if (shape.religious_level_to) { - shape.religious_level_to = JSON.parse(shape.religious_level_to.replace(/'/g, '"')); - } - if (shape.military_level_from) { - shape.military_level_from = JSON.parse(shape.military_level_from.replace(/'/g, '"')); - } - if (shape.military_level_to) { - shape.military_level_to = JSON.parse(shape.military_level_to.replace(/'/g, '"')); - } - if (shape.administrative_level_from) { - shape.administrative_level_from = JSON.parse(shape.administrative_level_from.replace(/'/g, '"')); - } - if (shape.administrative_level_to) { - shape.administrative_level_to = JSON.parse(shape.administrative_level_to.replace(/'/g, '"')); + if (shape.administrative_level) { + shape.administrative_level = JSON.parse(shape.administrative_level.replace(/'/g, '"')); } // Any fields that end _dict should be converted to a dictionary from a string for (const [key, value] of Object.entries(shape)) { @@ -1006,9 +994,9 @@

How to use the Seshat World Map

} // Hierarchical complexity variables - } else if (variable == 'settlement_hierarchy_from' || 'settlement_hierarchy_to' || variable == 'religious_level_from' || 'religious_level_to' || variable == 'military_level_from' || 'military_level_to' || variable == 'administrative_level_from' || 'administrative_level_to') { + } else if (variable == 'settlement_hierarchy' || variable == 'religious_level' || variable == 'military_level' || variable == 'administrative_level') { shapeWeight = polityBorderWeightSelected; - shapeColour = hierarchicalComplexityColourMapping[shape[variable]]; + shapeColour = hierarchicalComplexityColourMapping[shape[variable][0]]; // TODO: Make this an average } else { // Absent-present variables shapeWeight = polityBorderWeightSelected; @@ -1251,35 +1239,35 @@

How to use the Seshat World Map

`; } - if (variable == 'settlement_hierarchy_from' || variable == 'settlement_hierarchy_to') { + if (variable == 'settlement_hierarchy') { popupContent = popupContent + ` Settlement Hierarchy - ${shape.settlement_hierarchy_from} to ${shape.settlement_hierarchy_to} + ${shape.settlement_hierarchy[0]} to ${shape.settlement_hierarchy[1]} `; } - if (variable == 'religious_level_from' || variable == 'religious_level_to') { + if (variable == 'religious_level') { popupContent = popupContent + ` Religious Level - ${shape.religious_level_from} to ${shape.religious_level_to} + ${shape.religious_level[0]} to ${shape.religious_level[1]} `; } - if (variable == 'military_level_from' || variable == 'military_level_to') { + if (variable == 'military_level') { popupContent = popupContent + ` Military Level - ${shape.military_level_from} to ${shape.military_level_to} + ${shape.military_level[0]} to ${shape.military_level[1]} `; } - if (variable == 'administrative_level_from' || variable == 'administrative_level_to') { + if (variable == 'administrative_level') { popupContent = popupContent + ` Administrative Level - ${shape.administrative_level_from} to ${shape.administrative_level_to} + ${shape.administrative_level[0]} to ${shape.administrative_level[1]} `; } From 61f9bd51bea6457e2dac8594970ce6cd55bb719c Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 11:15:01 +0000 Subject: [PATCH 09/30] Refactor world map template to use hierarchical complexity variables list for improved readability --- seshat/apps/core/templates/core/world_map.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 07258b548..968d807f8 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -875,6 +875,7 @@

How to use the Seshat World Map

var playButton = document.getElementById('playButton'); var settings = document.getElementById('settings'); var help = document.getElementById('help'); + var hierarchicalComplexityVariables = ['military_level', 'religious_level', 'settlement_hierarchy', 'administrative_level']; // If not all polities loaded and the url year does not match the selected year, restore the loading indicator if (!allPolitiesLoaded) { @@ -994,7 +995,7 @@

How to use the Seshat World Map

} // Hierarchical complexity variables - } else if (variable == 'settlement_hierarchy' || variable == 'religious_level' || variable == 'military_level' || variable == 'administrative_level') { + } else if (hierarchicalComplexityVariables.includes(variable)) { shapeWeight = polityBorderWeightSelected; shapeColour = hierarchicalComplexityColourMapping[shape[variable][0]]; // TODO: Make this an average @@ -1130,7 +1131,7 @@

How to use the Seshat World Map

`; // Absent/present variables - if (variable != 'polity' && !categorical_variables.hasOwnProperty(variable)) { + if (variable != 'polity' && !categorical_variables.hasOwnProperty(variable) && !hierarchicalComplexityVariables.includes(variable)) { if (shape[variable + '_dict']) { // Iterate through key/values in the dictionary var counter = 0; @@ -1243,7 +1244,7 @@

How to use the Seshat World Map

popupContent = popupContent + ` Settlement Hierarchy - ${shape.settlement_hierarchy[0]} to ${shape.settlement_hierarchy[1]} + ${shape.settlement_hierarchy[0]} to ${shape.settlement_hierarchy[1]} `; } @@ -1251,7 +1252,7 @@

How to use the Seshat World Map

popupContent = popupContent + ` Religious Level - ${shape.religious_level[0]} to ${shape.religious_level[1]} + ${shape.religious_level[0]} to ${shape.religious_level[1]} `; } @@ -1259,7 +1260,7 @@

How to use the Seshat World Map

popupContent = popupContent + ` Military Level - ${shape.military_level[0]} to ${shape.military_level[1]} + ${shape.military_level[0]} to ${shape.military_level[1]} `; } @@ -1267,7 +1268,7 @@

How to use the Seshat World Map

popupContent = popupContent + ` Administrative Level - ${shape.administrative_level[0]} to ${shape.administrative_level[1]} + ${shape.administrative_level[0]} to ${shape.administrative_level[1]} `; } From 72be641aac595431e8854110863260417e867f90 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 11:18:50 +0000 Subject: [PATCH 10/30] Refactor world map popup content to display single values for hierarchy and levels when they are equal --- .../apps/core/templates/core/world_map.html | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 968d807f8..a198a3f3a 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1241,36 +1241,72 @@

How to use the Seshat World Map

`; } if (variable == 'settlement_hierarchy') { - popupContent = popupContent + ` - - Settlement Hierarchy - ${shape.settlement_hierarchy[0]} to ${shape.settlement_hierarchy[1]} - - `; + if (shape.settlement_hierarchy[0] == shape.settlement_hierarchy[1]) { + popupContent = popupContent + ` + + Settlement Hierarchy + ${shape.settlement_hierarchy[0]} + + `; + } else { + popupContent = popupContent + ` + + Settlement Hierarchy + ${shape.settlement_hierarchy[0]} to ${shape.settlement_hierarchy[1]} + + `; + } } if (variable == 'religious_level') { - popupContent = popupContent + ` - - Religious Level - ${shape.religious_level[0]} to ${shape.religious_level[1]} - - `; + if (shape.religious_level[0] == shape.religious_level[1]) { + popupContent = popupContent + ` + + Religious Level + ${shape.religious_level[0]} + + `; + } else { + popupContent = popupContent + ` + + Religious Level + ${shape.religious_level[0]} to ${shape.religious_level[1]} + + `; + } } if (variable == 'military_level') { - popupContent = popupContent + ` - - Military Level - ${shape.military_level[0]} to ${shape.military_level[1]} - - `; + if (shape.military_level[0] == shape.military_level[1]) { + popupContent = popupContent + ` + + Military Level + ${shape.military_level[0]} + + `; + } else { + popupContent = popupContent + ` + + Military Level + ${shape.military_level[0]} to ${shape.military_level[1]} + + `; + } } if (variable == 'administrative_level') { - popupContent = popupContent + ` - - Administrative Level - ${shape.administrative_level[0]} to ${shape.administrative_level[1]} - - `; + if (shape.administrative_level[0] == shape.administrative_level[1]) { + popupContent = popupContent + ` + + Administrative Level + ${shape.administrative_level[0]} + + `; + } else { + popupContent = popupContent + ` + + Administrative Level + ${shape.administrative_level[0]} to ${shape.administrative_level[1]} + + `; + } } if (allCapitalsInfo[shape.seshat_id]) { if (allCapitalsInfo[shape.seshat_id]['year_from'] <= selectedYearInt && allCapitalsInfo[shape.seshat_id]['year_to'] >= selectedYearInt) { From 2e18590e86fcfe102135baaeb33554bd52c4accc Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 11:22:52 +0000 Subject: [PATCH 11/30] Add uncoded fallback for settlement, religious, military, and administrative levels in world map popup --- .../apps/core/templates/core/world_map.html | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index a198a3f3a..4e56ffccc 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1248,6 +1248,13 @@

How to use the Seshat World Map

${shape.settlement_hierarchy[0]} `; + } else if (shape.settlement_hierarchy.length == 0) { + popupContent = popupContent + ` + + Settlement Hierarchy + Uncoded + + `; } else { popupContent = popupContent + ` @@ -1265,6 +1272,13 @@

How to use the Seshat World Map

${shape.religious_level[0]} `; + } else if (shape.religious_level.length == 0) { + popupContent = popupContent + ` + + Religious Level + Uncoded + + `; } else { popupContent = popupContent + ` @@ -1282,6 +1296,13 @@

How to use the Seshat World Map

${shape.military_level[0]} `; + } else if (shape.military_level.length == 0) { + popupContent = popupContent + ` + + Military Level + Uncoded + + `; } else { popupContent = popupContent + ` @@ -1299,6 +1320,13 @@

How to use the Seshat World Map

${shape.administrative_level[0]} `; + } else if (shape.administrative_level.length == 0) { + popupContent = popupContent + ` + + Administrative Level + Uncoded + + `; } else { popupContent = popupContent + ` From c7b9f76af06f879bb3a5ae85493b65b8c274ed16 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 11:29:52 +0000 Subject: [PATCH 12/30] Fix uncoded fallback for hierarchy and levels in world map popup --- .../apps/core/templates/core/world_map.html | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 4e56ffccc..de8ac989f 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1241,18 +1241,18 @@

How to use the Seshat World Map

`; } if (variable == 'settlement_hierarchy') { - if (shape.settlement_hierarchy[0] == shape.settlement_hierarchy[1]) { + if (shape.settlement_hierarchy.length == 0) { popupContent = popupContent + ` Settlement Hierarchy - ${shape.settlement_hierarchy[0]} + Uncoded `; - } else if (shape.settlement_hierarchy.length == 0) { + } else if (shape.settlement_hierarchy[0] == shape.settlement_hierarchy[1]) { popupContent = popupContent + ` Settlement Hierarchy - Uncoded + ${shape.settlement_hierarchy[0]} `; } else { @@ -1265,18 +1265,18 @@

How to use the Seshat World Map

} } if (variable == 'religious_level') { - if (shape.religious_level[0] == shape.religious_level[1]) { + if (shape.religious_level.length == 0) { popupContent = popupContent + ` Religious Level - ${shape.religious_level[0]} + Uncoded `; - } else if (shape.religious_level.length == 0) { + } else if (shape.religious_level[0] == shape.religious_level[1]) { popupContent = popupContent + ` Religious Level - Uncoded + ${shape.religious_level[0]} `; } else { @@ -1289,18 +1289,18 @@

How to use the Seshat World Map

} } if (variable == 'military_level') { - if (shape.military_level[0] == shape.military_level[1]) { + if (shape.military_level.length == 0) { popupContent = popupContent + ` Military Level - ${shape.military_level[0]} + Uncoded `; - } else if (shape.military_level.length == 0) { + } else if (shape.military_level[0] == shape.military_level[1]) { popupContent = popupContent + ` Military Level - Uncoded + ${shape.military_level[0]} `; } else { @@ -1313,18 +1313,18 @@

How to use the Seshat World Map

} } if (variable == 'administrative_level') { - if (shape.administrative_level[0] == shape.administrative_level[1]) { + if (shape.administrative_level.length == 0) { popupContent = popupContent + ` Administrative Level - ${shape.administrative_level[0]} + Uncoded `; - } else if (shape.administrative_level.length == 0) { + } else if (shape.administrative_level[0] == shape.administrative_level[1]) { popupContent = popupContent + ` Administrative Level - Uncoded + ${shape.administrative_level[0]} `; } else { From f9ccf6abf5af3bc73659333958e7e8d3e22c0275 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 13:38:59 +0000 Subject: [PATCH 13/30] add continuous colour scale for hierarchical complexity vars --- .../apps/core/templates/core/world_map.html | 58 +++++++++++++------ seshat/apps/core/views.py | 6 ++ 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index de8ac989f..b3f8c3aa6 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -714,6 +714,11 @@

How to use the Seshat World Map

var tick_years = {{ tick_years }}; setSliderTicks(tick_years); + var highestSettlementHierarchy; + var highestReligiousLevel; + var highestMilitaryLevel; + var highestAdministrativeLevel; + // Load all polity shapes and modern province/country shapes in background window.addEventListener('load', function () { function fetchData(url, displayYear) { @@ -763,6 +768,12 @@

How to use the Seshat World Map

plotPolities(); document.getElementById('variablesLoadingIndicator').style.display = 'none'; + // Get the highest values of the hierarchical complexity variables + highestSettlementHierarchy = data.highest_settlement_hierarchy; + highestReligiousLevel = data.highest_religious_level; + highestMilitaryLevel = data.highest_military_level; + highestAdministrativeLevel = data.highest_administrative_level; + return fetch('/core/provinces_and_countries'); }) .then(response => response.json()) @@ -826,23 +837,23 @@

How to use the Seshat World Map

'No Seshat page': 'silver' }; - let hierarchicalComplexityColourMapping = { - 0: 'grey', - 1: '#ff9c9c', - 2: '#ffc68c', - 3: '#82c1c1', - 4: 'orange', - 5: 'blue', - 6: 'purple', - 7: 'green', - 8: 'red', - 9: 'yellow', - 10: 'brown', - 11: 'pink', - 12: 'cyan', - 13: 'magenta', - 14: 'teal' - }; + function hierarchicalComplexityColour(maxValue, value) { + // If the value is null, return silver + if (value == null) { + return 'silver'; + } + // If the value is 0, return white + if (value == 0) { + return 'white'; + } + // If the value is greater than the maximum value, return red + if (value > maxValue) { + return 'red'; + } + // Calculate the colour based on the value and the maximum value + let hue = (1 - value / maxValue) * 120; + return `hsl(${hue}, 100%, 50%)`; + } function switchBaseMapWorldMap() { switchBaseMap(); @@ -997,7 +1008,18 @@

How to use the Seshat World Map

// Hierarchical complexity variables } else if (hierarchicalComplexityVariables.includes(variable)) { shapeWeight = polityBorderWeightSelected; - shapeColour = hierarchicalComplexityColourMapping[shape[variable][0]]; // TODO: Make this an average + // Get the maximum value of the hierarchical complexity variable + let hierarchicalVariableMaxValue; + if (variable == 'settlement_hierarchy') { + hierarchicalVariableMaxValue = highestSettlementHierarchy; + } else if (variable == 'religious_level') { + hierarchicalVariableMaxValue = highestReligiousLevel; + } else if (variable == 'military_level') { + hierarchicalVariableMaxValue = highestMilitaryLevel; + } else if (variable == 'administrative_level') { + hierarchicalVariableMaxValue = highestAdministrativeLevel; + } + shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][0]); // TODO: Make this an average } else { // Absent-present variables shapeWeight = polityBorderWeightSelected; diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index f40a085cc..ce11407b1 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -4640,6 +4640,12 @@ def map_view_all_with_vars(request): # Set the last year in history we ever want to display, which will be used to determine when we should say "present" content['last_history_year'] = content['latest_year'] # Set this to the latest year in the data or a value of choice + # Get the highest values of hierarchical complexity variables for the legend + content['highest_settlement_hierarchy'] = max([max(filter(None, shape['settlement_hierarchy']), default=0) for shape in content['shapes'] if shape['settlement_hierarchy']], default=0) + content['highest_religious_level'] = max([max(filter(None, shape['religious_level']), default=0) for shape in content['shapes'] if shape['religious_level']], default=0) + content['highest_military_level'] = max([max(filter(None, shape['military_level']), default=0) for shape in content['shapes'] if shape['military_level']], default=0) + content['highest_administrative_level'] = max([max(filter(None, shape['administrative_level']), default=0) for shape in content['shapes'] if shape['administrative_level']], default=0) + return JsonResponse(content) def provinces_and_countries_view(request): From 8f34341bc84ecc7a58af14852fedd9df40b86ffd Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 13:43:29 +0000 Subject: [PATCH 14/30] Move hierarchical complexity color function to map_functions --- .../apps/core/static/core/js/map_functions.js | 18 ++++++++++++++++++ seshat/apps/core/templates/core/world_map.html | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 0d33d000e..95388757b 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -827,4 +827,22 @@ function closeHelp() { if (popup !== null) { popup.style.display = 'block'; } +} + +function hierarchicalComplexityColour(maxValue, value) { + // If the value is null, return silver (Uncoded) + if (value == null) { + return 'silver'; + } + // If the value is 0, return white + if (value == 0) { + return 'white'; + } + // If the value is greater than the maximum value, return red + if (value > maxValue) { + return 'red'; + } + // Calculate the colour based on the value and the maximum value + let hue = (1 - value / maxValue) * 120; + return `hsl(${hue}, 100%, 50%)`; } \ No newline at end of file diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index b3f8c3aa6..7aba0c088 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -837,24 +837,6 @@

How to use the Seshat World Map

'No Seshat page': 'silver' }; - function hierarchicalComplexityColour(maxValue, value) { - // If the value is null, return silver - if (value == null) { - return 'silver'; - } - // If the value is 0, return white - if (value == 0) { - return 'white'; - } - // If the value is greater than the maximum value, return red - if (value > maxValue) { - return 'red'; - } - // Calculate the colour based on the value and the maximum value - let hue = (1 - value / maxValue) * 120; - return `hsl(${hue}, 100%, 50%)`; - } - function switchBaseMapWorldMap() { switchBaseMap(); if (document.getElementById('chooseVariable').value != 'polity') { From 37246f7db30fdc6846a6558ab981bd15555a5769 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 13:47:36 +0000 Subject: [PATCH 15/30] Remove duplicate declaration of hierarchical complexity variables in world map template --- seshat/apps/core/templates/core/world_map.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 7aba0c088..d1cbefe8d 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -614,6 +614,8 @@

How to use the Seshat World Map

updateCategoricalVariableSelection(localStorage.getItem('variable')); }; } + + var hierarchicalComplexityVariables = ['military_level', 'religious_level', 'settlement_hierarchy', 'administrative_level']; var displayedShapes = []; var polityBorderWeight = 0; @@ -868,7 +870,6 @@

How to use the Seshat World Map

var playButton = document.getElementById('playButton'); var settings = document.getElementById('settings'); var help = document.getElementById('help'); - var hierarchicalComplexityVariables = ['military_level', 'religious_level', 'settlement_hierarchy', 'administrative_level']; // If not all polities loaded and the url year does not match the selected year, restore the loading indicator if (!allPolitiesLoaded) { From 2543ed34714323ef31e6b6dcd5c2d1e94291e412 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 14:16:53 +0000 Subject: [PATCH 16/30] Add legend items for hierarchical complexity variables in map legend --- .../apps/core/static/core/js/map_functions.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 95388757b..6aa1d0763 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -410,6 +410,37 @@ function updateLegend() { legendDiv.appendChild(polityContainer); } + } else if (hierarchicalComplexityVariables.includes(variable)) { + + var legendTitle = document.createElement('h3'); + legendTitle.textContent = variable; + legendDiv.appendChild(legendTitle); + + let hierarchicalVariableMaxValue; + if (variable == 'settlement_hierarchy') { + hierarchicalVariableMaxValue = highestSettlementHierarchy; + } else if (variable == 'religious_level') { + hierarchicalVariableMaxValue = highestReligiousLevel; + } else if (variable == 'military_level') { + hierarchicalVariableMaxValue = highestMilitaryLevel; + } else if (variable == 'administrative_level') { + hierarchicalVariableMaxValue = highestAdministrativeLevel; + } + + for (var i = 0; i <= hierarchicalVariableMaxValue; i++) { + var legendItem = document.createElement('p'); + var colorBox = document.createElement('span'); + colorBox.style.display = 'inline-block'; + colorBox.style.width = '20px'; + colorBox.style.height = '20px'; + colorBox.style.backgroundColor = hierarchicalComplexityColour(hierarchicalVariableMaxValue, i); + colorBox.style.border = '1px solid black'; + colorBox.style.marginRight = '10px'; + legendItem.appendChild(colorBox); + legendItem.appendChild(document.createTextNode(i)); + legendDiv.appendChild(legendItem); + } + } else if (variable in categorical_variables) { var legendTitle = document.createElement('h3'); From 038198a448f939852f7f11807a42a38c912b3632 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 14:50:10 +0000 Subject: [PATCH 17/30] Update legend colors and modify color function for hierarchical complexity --- .../apps/core/static/core/js/map_functions.js | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 6aa1d0763..ac25ec30b 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -427,19 +427,29 @@ function updateLegend() { hierarchicalVariableMaxValue = highestAdministrativeLevel; } - for (var i = 0; i <= hierarchicalVariableMaxValue; i++) { - var legendItem = document.createElement('p'); - var colorBox = document.createElement('span'); - colorBox.style.display = 'inline-block'; - colorBox.style.width = '20px'; - colorBox.style.height = '20px'; - colorBox.style.backgroundColor = hierarchicalComplexityColour(hierarchicalVariableMaxValue, i); - colorBox.style.border = '1px solid black'; - colorBox.style.marginRight = '10px'; - legendItem.appendChild(colorBox); - legendItem.appendChild(document.createTextNode(i)); - legendDiv.appendChild(legendItem); - } + var legendItem = document.createElement('p'); + var colorBox = document.createElement('span'); + colorBox.style.display = 'inline-block'; + colorBox.style.width = '20px'; + colorBox.style.height = '20px'; + colorBox.style.backgroundColor = 'blue'; + colorBox.style.border = '1px solid black'; + colorBox.style.marginRight = '10px'; + legendItem.appendChild(colorBox); + legendItem.appendChild(document.createTextNode(0)); + legendDiv.appendChild(legendItem); + + var legendItem = document.createElement('p'); + var colorBox = document.createElement('span'); + colorBox.style.display = 'inline-block'; + colorBox.style.width = '20px'; + colorBox.style.height = '20px'; + colorBox.style.backgroundColor = 'red'; + colorBox.style.border = '1px solid black'; + colorBox.style.marginRight = '10px'; + legendItem.appendChild(colorBox); + legendItem.appendChild(document.createTextNode(hierarchicalVariableMaxValue)); + legendDiv.appendChild(legendItem); } else if (variable in categorical_variables) { @@ -865,15 +875,18 @@ function hierarchicalComplexityColour(maxValue, value) { if (value == null) { return 'silver'; } - // If the value is 0, return white + // If the value is 0, return blue if (value == 0) { - return 'white'; + return 'blue'; } // If the value is greater than the maximum value, return red if (value > maxValue) { return 'red'; } // Calculate the colour based on the value and the maximum value - let hue = (1 - value / maxValue) * 120; - return `hsl(${hue}, 100%, 50%)`; + let ratio = value / maxValue; + let r = Math.round(255 * ratio); + let g = 0; + let b = Math.round(255 * (1 - ratio)); + return `rgb(${r}, ${g}, ${b})`; } \ No newline at end of file From 759787ea5c3e46d46b4fa040d29a89557f896dbc Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 15:17:00 +0000 Subject: [PATCH 18/30] Set hierarchicalComplexityColourMapping in world_map template --- .../apps/core/static/core/js/map_functions.js | 72 +++++++++++-------- .../apps/core/templates/core/world_map.html | 6 ++ 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index ac25ec30b..5e267721c 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -427,29 +427,28 @@ function updateLegend() { hierarchicalVariableMaxValue = highestAdministrativeLevel; } - var legendItem = document.createElement('p'); - var colorBox = document.createElement('span'); - colorBox.style.display = 'inline-block'; - colorBox.style.width = '20px'; - colorBox.style.height = '20px'; - colorBox.style.backgroundColor = 'blue'; - colorBox.style.border = '1px solid black'; - colorBox.style.marginRight = '10px'; - legendItem.appendChild(colorBox); - legendItem.appendChild(document.createTextNode(0)); - legendDiv.appendChild(legendItem); + for (var key in hierarchicalComplexityColourMapping) { - var legendItem = document.createElement('p'); - var colorBox = document.createElement('span'); - colorBox.style.display = 'inline-block'; - colorBox.style.width = '20px'; - colorBox.style.height = '20px'; - colorBox.style.backgroundColor = 'red'; - colorBox.style.border = '1px solid black'; - colorBox.style.marginRight = '10px'; - legendItem.appendChild(colorBox); - legendItem.appendChild(document.createTextNode(hierarchicalVariableMaxValue)); - legendDiv.appendChild(legendItem); + var legendItem = document.createElement('p'); + + var colorBox = document.createElement('span'); + colorBox.style.display = 'inline-block'; + colorBox.style.width = '20px'; + colorBox.style.height = '20px'; + colorBox.style.backgroundColor = hierarchicalComplexityColourMapping[key]; + colorBox.style.marginRight = '10px'; + legendItem.appendChild(colorBox); + + if (key === 'min') { + legendItem.appendChild(document.createTextNode(0)); + } else if (key === 'max') { + legendItem.appendChild(document.createTextNode(hierarchicalVariableMaxValue)); + } else { + legendItem.appendChild(document.createTextNode(`${key}`)); + } + + legendDiv.appendChild(legendItem); + }; } else if (variable in categorical_variables) { @@ -875,18 +874,33 @@ function hierarchicalComplexityColour(maxValue, value) { if (value == null) { return 'silver'; } - // If the value is 0, return blue + // If the value is 0, return the min color if (value == 0) { - return 'blue'; + return hierarchicalComplexityColourMapping['min']; } - // If the value is greater than the maximum value, return red + // If the value is greater than the maximum value, return the max color if (value > maxValue) { - return 'red'; + return hierarchicalComplexityColourMapping['max']; } // Calculate the colour based on the value and the maximum value let ratio = value / maxValue; - let r = Math.round(255 * ratio); - let g = 0; - let b = Math.round(255 * (1 - ratio)); + + // Convert hex to RGB + function hexToRgb(hex) { + let bigint = parseInt(hex.slice(1), 16); + return { + r: (bigint >> 16) & 255, + g: (bigint >> 8) & 255, + b: bigint & 255 + }; + } + + let startColor = hexToRgb(hierarchicalComplexityColourMapping['min']); + let endColor = hexToRgb(hierarchicalComplexityColourMapping['max']); + + let r = Math.round(startColor.r + ratio * (endColor.r - startColor.r)); + let g = Math.round(startColor.g + ratio * (endColor.g - startColor.g)); + let b = Math.round(startColor.b + ratio * (endColor.b - startColor.b)); + return `rgb(${r}, ${g}, ${b})`; } \ No newline at end of file diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index d1cbefe8d..6a86882ad 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -839,6 +839,12 @@

How to use the Seshat World Map

'No Seshat page': 'silver' }; + let hierarchicalComplexityColourMapping = { + 'min': 'blue', + 'max': 'red', + 'Uncoded': 'silver' + }; + function switchBaseMapWorldMap() { switchBaseMap(); if (document.getElementById('chooseVariable').value != 'polity') { From b53baabef1804efc1178d6a01431d8c2c8833978 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 15:36:02 +0000 Subject: [PATCH 19/30] Update hierarchical complexity color mapping to use hex codes --- seshat/apps/core/templates/core/world_map.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 6a86882ad..c857895fe 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -840,8 +840,8 @@

How to use the Seshat World Map

}; let hierarchicalComplexityColourMapping = { - 'min': 'blue', - 'max': 'red', + 'min': '#0000FF', // blue + 'max': '#FF0000', // red 'Uncoded': 'silver' }; From f3deccbef77f258176db5fedf960fb181902da19 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 8 Jan 2025 15:48:26 +0000 Subject: [PATCH 20/30] Refactor shapeColour calculation to prioritize 'to' value over 'from' value for hierarchical complexity --- seshat/apps/core/templates/core/world_map.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index c857895fe..971492c79 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1008,7 +1008,12 @@

How to use the Seshat World Map

} else if (variable == 'administrative_level') { hierarchicalVariableMaxValue = highestAdministrativeLevel; } - shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][0]); // TODO: Make this an average + // Calculate the colour based highest of the from and to values + if (shape[variable][1] != 'None' && shape[variable][1] != null) { // If the to value is not None, use it since it is the highest + shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][1]); + } else { // If the to value is None, use the from value (even if it is None) + shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][0]); + } } else { // Absent-present variables shapeWeight = polityBorderWeightSelected; From 86b61e3f747b89081da0af4eecdc5946796322b7 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 11:19:43 +0000 Subject: [PATCH 21/30] Sort variables alphabetically within each category in the dropdown --- seshat/apps/core/static/core/js/map_functions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 5e267721c..ef3a0e1e8 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -747,6 +747,7 @@ function populateVariableDropdown(variables) { option.textContent = details.full_name; optgroup.appendChild(option); }); + optgroup.innerHTML = [...optgroup.children].sort((a, b) => a.textContent.localeCompare(b.textContent)).map(e => e.outerHTML).join(''); chooseVariableDropdown.appendChild(optgroup); } }); From 2e8d4b149ef87875e5775bb7d1e088209b6e3ead Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 11:24:25 +0000 Subject: [PATCH 22/30] Add prefix to dropdown options for hierarchical complexity variables --- seshat/apps/core/static/core/js/map_functions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index ef3a0e1e8..c696e71dc 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -744,7 +744,11 @@ function populateVariableDropdown(variables) { Object.entries(vars).forEach(([variable, details]) => { const option = document.createElement('option'); option.value = details.formatted; - option.textContent = details.full_name; + if (hierarchicalComplexityVariables.includes(variable)) { + option.textContent = "Hierarchical Complexity: " + details.full_name; + } else { + option.textContent = details.full_name; + } optgroup.appendChild(option); }); optgroup.innerHTML = [...optgroup.children].sort((a, b) => a.textContent.localeCompare(b.textContent)).map(e => e.outerHTML).join(''); From a77e10dbbf0154b0d2ed6e694dd410e71932b431 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 12:59:50 +0000 Subject: [PATCH 23/30] handle different naming formats --- .../apps/core/static/core/js/map_functions.js | 10 ++-- .../apps/core/templates/core/world_map.html | 54 ++++++++++--------- seshat/apps/core/views.py | 8 +-- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index c696e71dc..8ccd5880e 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -410,20 +410,20 @@ function updateLegend() { legendDiv.appendChild(polityContainer); } - } else if (hierarchicalComplexityVariables.includes(variable)) { + } else if (hierarchicalComplexityVariablesFull.includes(variable)) { var legendTitle = document.createElement('h3'); legendTitle.textContent = variable; legendDiv.appendChild(legendTitle); let hierarchicalVariableMaxValue; - if (variable == 'settlement_hierarchy') { + if (variable == 'Settlement Hierarchy') { hierarchicalVariableMaxValue = highestSettlementHierarchy; - } else if (variable == 'religious_level') { + } else if (variable == 'Religious Hierarchy') { hierarchicalVariableMaxValue = highestReligiousLevel; - } else if (variable == 'military_level') { + } else if (variable == 'Military Hierarchy') { hierarchicalVariableMaxValue = highestMilitaryLevel; - } else if (variable == 'administrative_level') { + } else if (variable == 'Administrative Hierarchy') { hierarchicalVariableMaxValue = highestAdministrativeLevel; } diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 971492c79..8a10bbfe0 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -615,7 +615,8 @@

How to use the Seshat World Map

}; } - var hierarchicalComplexityVariables = ['military_level', 'religious_level', 'settlement_hierarchy', 'administrative_level']; + var hierarchicalComplexityVariablesFull = ['Military Level', 'Administrative Level', 'Religious Level', 'Settlement Hierarchy']; + var hierarchicalComplexityVariables = ['military_level', 'administrative_level', 'religious_level', 'settlement_hierarchy']; var displayedShapes = []; var polityBorderWeight = 0; @@ -940,6 +941,7 @@

How to use the Seshat World Map

var selectedYearInt = parseInt(selectedYear); // Add shapes to the map // Don't plot them if "Base map only" checkbox selected + console.log(variable); if (!document.getElementById('baseMapOnly').checked){ // Iterate through shapesData and plot the shapes @@ -953,6 +955,27 @@

How to use the Seshat World Map

shapeWeight = shape.weight; shapeColour = shape.colour; + // Hierarchical complexity variables + } else if (hierarchicalComplexityVariablesFull.includes(variable)) { + shapeWeight = polityBorderWeightSelected; + // Get the maximum value of the hierarchical complexity variable + let hierarchicalVariableMaxValue; + if (variable == 'Settlement Hierarchy') { + hierarchicalVariableMaxValue = highestSettlementHierarchy; + } else if (variable == 'Religious Level') { + hierarchicalVariableMaxValue = highestReligiousLevel; + } else if (variable == 'Military Level') { + hierarchicalVariableMaxValue = highestMilitaryLevel; + } else if (variable == 'Administrative Level') { + hierarchicalVariableMaxValue = highestAdministrativeLevel; + } + let variable_underscore = variable.toLowerCase().replace(' ', '_'); + // Calculate the colour based highest of the from and to values + if (shape[variable_underscore][1] != 'None' && shape[variable_underscore][1] != null) { // If the to value is not None, use it since it is the highest + shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable_underscore][1]); + } else { // If the to value is None, use the from value (even if it is None) + shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable_underscore][0]); + } } else if (variable in categorical_variables){ shapeWeight = polityBorderWeightSelected; // If the shape has a dictionary for the variable the key of the dict is the variable value and the value is a list of start and end years. @@ -993,27 +1016,6 @@

How to use the Seshat World Map

shapeColour = oneLanguageColourMapping['No Seshat page']; } } - - // Hierarchical complexity variables - } else if (hierarchicalComplexityVariables.includes(variable)) { - shapeWeight = polityBorderWeightSelected; - // Get the maximum value of the hierarchical complexity variable - let hierarchicalVariableMaxValue; - if (variable == 'settlement_hierarchy') { - hierarchicalVariableMaxValue = highestSettlementHierarchy; - } else if (variable == 'religious_level') { - hierarchicalVariableMaxValue = highestReligiousLevel; - } else if (variable == 'military_level') { - hierarchicalVariableMaxValue = highestMilitaryLevel; - } else if (variable == 'administrative_level') { - hierarchicalVariableMaxValue = highestAdministrativeLevel; - } - // Calculate the colour based highest of the from and to values - if (shape[variable][1] != 'None' && shape[variable][1] != null) { // If the to value is not None, use it since it is the highest - shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][1]); - } else { // If the to value is None, use the from value (even if it is None) - shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable][0]); - } } else { // Absent-present variables shapeWeight = polityBorderWeightSelected; @@ -1256,7 +1258,7 @@

How to use the Seshat World Map

`; } - if (variable == 'settlement_hierarchy') { + if (variable == 'Settlement Hierarchy') { if (shape.settlement_hierarchy.length == 0) { popupContent = popupContent + ` @@ -1280,7 +1282,7 @@

How to use the Seshat World Map

`; } } - if (variable == 'religious_level') { + if (variable == 'Religious Level') { if (shape.religious_level.length == 0) { popupContent = popupContent + ` @@ -1304,7 +1306,7 @@

How to use the Seshat World Map

`; } } - if (variable == 'military_level') { + if (variable == 'Military Level') { if (shape.military_level.length == 0) { popupContent = popupContent + ` @@ -1328,7 +1330,7 @@

How to use the Seshat World Map

`; } } - if (variable == 'administrative_level') { + if (variable == 'Administrative Level') { if (shape.administrative_level.length == 0) { popupContent = popupContent + ` diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index ce11407b1..225d7960b 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -4280,10 +4280,10 @@ def assign_categorical_variables_to_shapes(shapes, variables): # Add categorical variables from Social Complexity Variables to the variables dictionary if 'Social Complexity Variables' not in variables: variables['Social Complexity Variables'] = {} - variables['Social Complexity Variables']['settlement_hierarchy'] = {'formatted': 'settlement_hierarchy', 'full_name': 'Settlement Hierarchy'} - variables['Social Complexity Variables']['religious_level'] = {'formatted': 'religious_level', 'full_name': 'Religious Level'} - variables['Social Complexity Variables']['military_level'] = {'formatted': 'military_level', 'full_name': 'Military Level'} - variables['Social Complexity Variables']['administrative_level'] = {'formatted': 'administrative_level', 'full_name': 'Administrative Level'} + variables['Social Complexity Variables']['settlement_hierarchy'] = {'formatted': 'Settlement Hierarchy', 'full_name': 'Settlement Hierarchy'} + variables['Social Complexity Variables']['religious_level'] = {'formatted': 'Religious Level', 'full_name': 'Religious Level'} + variables['Social Complexity Variables']['military_level'] = {'formatted': 'Military Level', 'full_name': 'Military Level'} + variables['Social Complexity Variables']['administrative_level'] = {'formatted': 'Administrative Level', 'full_name': 'Administrative Level'} # Fetch all polities and store them in a dictionary for quick access polities = {polity.new_name: polity for polity in Polity.objects.all()} From 1f6cb29939764a91d6ae8562ef6c9284179cce55 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 13:16:28 +0000 Subject: [PATCH 24/30] Remove unnecessary console log for variable in world_map.html --- seshat/apps/core/templates/core/world_map.html | 1 - 1 file changed, 1 deletion(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 8a10bbfe0..adedfd030 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -941,7 +941,6 @@

How to use the Seshat World Map

var selectedYearInt = parseInt(selectedYear); // Add shapes to the map // Don't plot them if "Base map only" checkbox selected - console.log(variable); if (!document.getElementById('baseMapOnly').checked){ // Iterate through shapesData and plot the shapes From c602146e2b1b8fb153c904c2de38707ceea9549c Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 13:21:06 +0000 Subject: [PATCH 25/30] fix var names --- seshat/apps/core/static/core/js/map_functions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 8ccd5880e..3cc9f4a2c 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -419,11 +419,11 @@ function updateLegend() { let hierarchicalVariableMaxValue; if (variable == 'Settlement Hierarchy') { hierarchicalVariableMaxValue = highestSettlementHierarchy; - } else if (variable == 'Religious Hierarchy') { + } else if (variable == 'Religious Level') { hierarchicalVariableMaxValue = highestReligiousLevel; - } else if (variable == 'Military Hierarchy') { + } else if (variable == 'Military Level') { hierarchicalVariableMaxValue = highestMilitaryLevel; - } else if (variable == 'Administrative Hierarchy') { + } else if (variable == 'Administrative Level') { hierarchicalVariableMaxValue = highestAdministrativeLevel; } From a6e5d693b2d1fe7521f176116545b9909803c213 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 13:40:40 +0000 Subject: [PATCH 26/30] Refactor hierarchical complexity variable handling to use a dict --- .../apps/core/static/core/js/map_functions.js | 13 +++---------- seshat/apps/core/templates/core/world_map.html | 18 +++--------------- seshat/apps/core/views.py | 9 +++++---- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 3cc9f4a2c..4257bdb11 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -416,16 +416,9 @@ function updateLegend() { legendTitle.textContent = variable; legendDiv.appendChild(legendTitle); - let hierarchicalVariableMaxValue; - if (variable == 'Settlement Hierarchy') { - hierarchicalVariableMaxValue = highestSettlementHierarchy; - } else if (variable == 'Religious Level') { - hierarchicalVariableMaxValue = highestReligiousLevel; - } else if (variable == 'Military Level') { - hierarchicalVariableMaxValue = highestMilitaryLevel; - } else if (variable == 'Administrative Level') { - hierarchicalVariableMaxValue = highestAdministrativeLevel; - } + let variable_underscore = variable.toLowerCase().replace(' ', '_'); + // Get the maximum value of the hierarchical complexity variable + let hierarchicalVariableMaxValue = highestComplexityValues[variable_underscore]; for (var key in hierarchicalComplexityColourMapping) { diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index adedfd030..7e519cf0f 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -772,10 +772,7 @@

How to use the Seshat World Map

document.getElementById('variablesLoadingIndicator').style.display = 'none'; // Get the highest values of the hierarchical complexity variables - highestSettlementHierarchy = data.highest_settlement_hierarchy; - highestReligiousLevel = data.highest_religious_level; - highestMilitaryLevel = data.highest_military_level; - highestAdministrativeLevel = data.highest_administrative_level; + highestComplexityValues = data.highest_complexity_values; return fetch('/core/provinces_and_countries'); }) @@ -957,18 +954,9 @@

How to use the Seshat World Map

// Hierarchical complexity variables } else if (hierarchicalComplexityVariablesFull.includes(variable)) { shapeWeight = polityBorderWeightSelected; - // Get the maximum value of the hierarchical complexity variable - let hierarchicalVariableMaxValue; - if (variable == 'Settlement Hierarchy') { - hierarchicalVariableMaxValue = highestSettlementHierarchy; - } else if (variable == 'Religious Level') { - hierarchicalVariableMaxValue = highestReligiousLevel; - } else if (variable == 'Military Level') { - hierarchicalVariableMaxValue = highestMilitaryLevel; - } else if (variable == 'Administrative Level') { - hierarchicalVariableMaxValue = highestAdministrativeLevel; - } let variable_underscore = variable.toLowerCase().replace(' ', '_'); + // Get the maximum value of the hierarchical complexity variable + let hierarchicalVariableMaxValue = highestComplexityValues[variable_underscore]; // Calculate the colour based highest of the from and to values if (shape[variable_underscore][1] != 'None' && shape[variable_underscore][1] != null) { // If the to value is not None, use it since it is the highest shapeColour = hierarchicalComplexityColour(hierarchicalVariableMaxValue, shape[variable_underscore][1]); diff --git a/seshat/apps/core/views.py b/seshat/apps/core/views.py index 225d7960b..374b7e4b3 100644 --- a/seshat/apps/core/views.py +++ b/seshat/apps/core/views.py @@ -4641,10 +4641,11 @@ def map_view_all_with_vars(request): content['last_history_year'] = content['latest_year'] # Set this to the latest year in the data or a value of choice # Get the highest values of hierarchical complexity variables for the legend - content['highest_settlement_hierarchy'] = max([max(filter(None, shape['settlement_hierarchy']), default=0) for shape in content['shapes'] if shape['settlement_hierarchy']], default=0) - content['highest_religious_level'] = max([max(filter(None, shape['religious_level']), default=0) for shape in content['shapes'] if shape['religious_level']], default=0) - content['highest_military_level'] = max([max(filter(None, shape['military_level']), default=0) for shape in content['shapes'] if shape['military_level']], default=0) - content['highest_administrative_level'] = max([max(filter(None, shape['administrative_level']), default=0) for shape in content['shapes'] if shape['administrative_level']], default=0) + content['highest_complexity_values'] = {} + content['highest_complexity_values']['settlement_hierarchy'] = max([max(filter(None, shape['settlement_hierarchy']), default=0) for shape in content['shapes'] if shape['settlement_hierarchy']], default=0) + content['highest_complexity_values']['religious_level'] = max([max(filter(None, shape['religious_level']), default=0) for shape in content['shapes'] if shape['religious_level']], default=0) + content['highest_complexity_values']['military_level'] = max([max(filter(None, shape['military_level']), default=0) for shape in content['shapes'] if shape['military_level']], default=0) + content['highest_complexity_values']['administrative_level'] = max([max(filter(None, shape['administrative_level']), default=0) for shape in content['shapes'] if shape['administrative_level']], default=0) return JsonResponse(content) From c1124995123e7cb0bbb89389173feb296758262a Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 13:47:52 +0000 Subject: [PATCH 27/30] Don't hide legend when a variable other than 'polity' is selected in clearSelection function --- seshat/apps/core/static/core/js/map_functions.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seshat/apps/core/static/core/js/map_functions.js b/seshat/apps/core/static/core/js/map_functions.js index 4257bdb11..0db043ffc 100644 --- a/seshat/apps/core/static/core/js/map_functions.js +++ b/seshat/apps/core/static/core/js/map_functions.js @@ -634,6 +634,9 @@ function clearSelection() { }); document.getElementById('hideUnselected').checked = false; plotPolities(); + if (document.getElementById('chooseVariable').value != 'polity') { + legendDiv.style.display = 'block'; + }; } function selectAllCheckbox() { From 5a5f61a5dfa34e2d698182e0b6af88e7e2141bae Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 14:29:10 +0000 Subject: [PATCH 28/30] Update variable check to use hierarchicalComplexityVariablesFull in world_map.html --- seshat/apps/core/templates/core/world_map.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 7e519cf0f..dd56836c3 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1136,7 +1136,7 @@

How to use the Seshat World Map

`; // Absent/present variables - if (variable != 'polity' && !categorical_variables.hasOwnProperty(variable) && !hierarchicalComplexityVariables.includes(variable)) { + if (variable != 'polity' && !categorical_variables.hasOwnProperty(variable) && !hierarchicalComplexityVariablesFull.includes(variable)) { if (shape[variable + '_dict']) { // Iterate through key/values in the dictionary var counter = 0; From 38ee6ce6c31caef29b9b8700a174c2fd5eab40a3 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 14:54:03 +0000 Subject: [PATCH 29/30] Enhance condition checks for settlement, religious, military, and administrative levels in world_map.html to handle 'None' and null values --- seshat/apps/core/templates/core/world_map.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index dd56836c3..74287faa9 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1253,7 +1253,7 @@

How to use the Seshat World Map

Uncoded `; - } else if (shape.settlement_hierarchy[0] == shape.settlement_hierarchy[1]) { + } else if (shape.settlement_hierarchy[0] == shape.settlement_hierarchy[1] || shape.settlement_hierarchy[1] == 'None' || shape.settlement_hierarchy[1] == null) { popupContent = popupContent + ` Settlement Hierarchy @@ -1277,7 +1277,7 @@

How to use the Seshat World Map

Uncoded `; - } else if (shape.religious_level[0] == shape.religious_level[1]) { + } else if (shape.religious_level[0] == shape.religious_level[1] || shape.religious_level[1] == 'None' || shape.religious_level[1] == null) { popupContent = popupContent + ` Religious Level @@ -1301,7 +1301,7 @@

How to use the Seshat World Map

Uncoded `; - } else if (shape.military_level[0] == shape.military_level[1]) { + } else if (shape.military_level[0] == shape.military_level[1] || shape.military_level[1] == 'None' || shape.military_level[1] == null) { popupContent = popupContent + ` Military Level @@ -1325,7 +1325,7 @@

How to use the Seshat World Map

Uncoded `; - } else if (shape.administrative_level[0] == shape.administrative_level[1]) { + } else if (shape.administrative_level[0] == shape.administrative_level[1] || shape.administrative_level[1] == 'None' || shape.administrative_level[1] == null) { popupContent = popupContent + ` Administrative Level From a1977c328c3074146c9c18dd0f9dbab26207be7a Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 9 Jan 2025 15:00:29 +0000 Subject: [PATCH 30/30] Enhance condition checks for settlement, religious, military, and administrative levels in world_map.html to handle 'None' and null values --- seshat/apps/core/templates/core/world_map.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seshat/apps/core/templates/core/world_map.html b/seshat/apps/core/templates/core/world_map.html index 74287faa9..52614d940 100644 --- a/seshat/apps/core/templates/core/world_map.html +++ b/seshat/apps/core/templates/core/world_map.html @@ -1246,7 +1246,7 @@

How to use the Seshat World Map

`; } if (variable == 'Settlement Hierarchy') { - if (shape.settlement_hierarchy.length == 0) { + if (shape.settlement_hierarchy.length == 0 || ((shape.settlement_hierarchy[0] == 'None' || shape.settlement_hierarchy[0] == null) && (shape.settlement_hierarchy[1] == 'None' || shape.settlement_hierarchy[1] == null))) { popupContent = popupContent + ` Settlement Hierarchy @@ -1270,7 +1270,7 @@

How to use the Seshat World Map

} } if (variable == 'Religious Level') { - if (shape.religious_level.length == 0) { + if (shape.religious_level.length == 0 || ((shape.religious_level[0] == 'None' || shape.religious_level[0] == null) && (shape.religious_level[1] == 'None' || shape.religious_level[1] == null))) { popupContent = popupContent + ` Religious Level @@ -1294,7 +1294,7 @@

How to use the Seshat World Map

} } if (variable == 'Military Level') { - if (shape.military_level.length == 0) { + if (shape.military_level.length == 0 || ((shape.military_level[0] == 'None' || shape.military_level[0] == null) && (shape.military_level[1] == 'None' || shape.military_level[1] == null))) { popupContent = popupContent + ` Military Level @@ -1318,7 +1318,7 @@

How to use the Seshat World Map

} } if (variable == 'Administrative Level') { - if (shape.administrative_level.length == 0) { + if (shape.administrative_level.length == 0 || ((shape.administrative_level[0] == 'None' || shape.administrative_level[0] == null) && (shape.administrative_level[1] == 'None' || shape.administrative_level[1] == null))) { popupContent = popupContent + ` Administrative Level