---
title: "SETTLEMENT GAZETTEER OF THE PONTIC STEPPE."
subtitle: "Supplementary Tabular Directory and Settlement Population Histories of the Lower Don, Azov Littoral, and Kuban-Stavropol Steppe (10000BC - 1900AD)."
date: "8 November 2026"
authors_line: "Aust Kätzchen"
emails:
  - austkaeztchen@confoederatio.org
hero_figure: "figures/pontic_steppe_regional_map.png"
hero_caption: "Cartographic basemap of the Russian Pontic Steppe (36,0°-44,8° E, 44,5°-50,5° N), showing administrative boundaries for Rostov Oblast and Krasnodar Krai and principal settlements catalogued in pontic_steppe_settlements.json."
hero_label: "fig:hero"
bibliography: pontic_steppe_literature_review.bib
csl: crd-mla-numeric.csl
link-citations: true
output:
  pdf_document:
    template: ../../../../core/rticles/intellte/resources/template.tex
    latex_engine: xelatex
    keep_tex: false
---

```{r setup, include=FALSE}
source("../../../../core/framework/R/framework_init.R")
```

```{r set-json-path, include=FALSE}
GAZETTEER_JSON_FILE <- "pontic_steppe_settlements.json"
```

```{r load-stadester-json, include=FALSE}
gaz_map <- jsonlite::fromJSON(GAZETTEER_JSON_FILE, simplifyVector = FALSE)
settlements_list <- unname(gaz_map)

# Sort chronologically by first occupied year, then alphabetically by name
first_year_num <- function (arg0_s) {
  yrs <- as.numeric(names(arg0_s$population))
  if (length(yrs) == 0) return(99999)
  return(min(yrs))
}
last_year_num <- function (arg0_s) {
  yrs <- as.numeric(names(arg0_s$population))
  if (length(yrs) == 0) return(-99999)
  return(max(yrs))
}

ord_idx <- order(
  vapply(settlements_list, first_year_num, numeric(1)),
  vapply(settlements_list, function (s) as.character(s$name), character(1))
)
settlements_list <- settlements_list[ord_idx]

translit_cyr <- function (arg0_str) {
  s <- as.character(arg0_str)
  if (!nzchar(s)) return("")
  # Map Cyrillic characters to Latin so Karla 10pt renders every character cleanly in XeLaTeX
  cyr <- c(
    "А","Б","В","Г","Д","Е","Ё","Ж","З","И","Й","К","Л","М","Н","О","П","Р","С","Т","У","Ф","Х","Ц","Ч","Ш","Щ","Ъ","Ы","Ь","Э","Ю","Я",
    "а","б","в","г","д","е","ё","ж","з","и","й","к","л","м","н","о","п","р","с","т","у","ф","х","ц","ч","ш","щ","ъ","ы","ь","э","ю","я",
    "Φ","α","ν","γ","ό","ρ","ε","ι","Κ","ῆ","π","ο","Πα","τ","ύ","ς","Τ","ά","ϊ","μ","β","η","К"
  )
  lat <- c(
    "A","B","V","G","D","E","Yo","Zh","Z","I","Y","K","L","M","N","O","P","R","S","T","U","F","Kh","Ts","Ch","Sh","Shch","","Y","","E","Yu","Ya",
    "a","b","v","g","d","e","yo","zh","z","i","y","k","l","m","n","o","p","r","s","t","u","f","kh","ts","ch","sh","shch","","y","","e","yu","ya",
    "Ph","a","n","g","o","r","e","i","K","e","p","o","Pa","t","y","s","T","a","i","m","b","e","K"
  )
  for (i in seq_along(cyr)) {
    s <- gsub(cyr[i], lat[i], s, fixed = TRUE)
  }
  return(s)
}

fmt_other_names <- function (arg0_s) {
  onames <- unlist(arg0_s$other_names)
  if (length(onames) == 0) return("-")
  clean_vec <- unique(vapply(onames, translit_cyr, character(1)))
  clean_vec <- clean_vec[nzchar(clean_vec) & tolower(clean_vec) != tolower(arg0_s$name)]
  if (length(clean_vec) == 0) {
    clean_vec <- unique(vapply(onames, translit_cyr, character(1)))
  }
  return(paste(head(clean_vec, 2L), collapse = "; "))
}

fmt_year_lbl <- function (arg0_y) {
  y <- as.numeric(arg0_y)
  if (y < 0) return(paste0(abs(round(y)), "BC"))
  return(paste0(round(y), "AD"))
}

fmt_span <- function (arg0_s) {
  y1 <- first_year_num(arg0_s)
  y2 <- last_year_num(arg0_s)
  if (y1 == y2) return(fmt_year_lbl(y1))
  if (y1 < 0 && y2 < 0) return(paste0(abs(y1), "-", abs(y2), "BC"))
  if (y1 > 0 && y2 > 0) return(paste0(y1, "-", y2, "AD"))
  return(paste0(fmt_year_lbl(y1), " - ", fmt_year_lbl(y2)))
}

fmt_num_eu <- function (arg0_val) {
  if (is.null(arg0_val) || is.na(arg0_val)) return("-")
  v <- as.numeric(arg0_val)
  if (v >= 1000) {
    s <- sprintf("%.1fk", v / 1000.0)
    s <- gsub("\\.", ",", s)
    return(s)
  }
  return(as.character(round(v)))
}

fmt_coord_pair <- function (arg0_coords) {
  lat <- gsub("\\.", ",", sprintf("%.2f", as.numeric(arg0_coords[[1]])))
  lon <- gsub("\\.", ",", sprintf("%.2f", as.numeric(arg0_coords[[2]])))
  return(paste0(lat, "°N, ", lon, "°E"))
}

fmt_pop_series <- function (arg0_s, arg1_ymin = -99999, arg2_ymax = 99999) {
  pop_map <- arg0_s$population
  yrs <- as.numeric(names(pop_map))
  vals <- as.numeric(unlist(pop_map))
  keep <- which(yrs >= arg1_ymin & yrs <= arg2_ymax)
  if (length(keep) == 0) return("-")
  parts <- vapply(keep, function (idx) {
    paste0(fmt_year_lbl(yrs[idx]), ": ", fmt_num_eu(vals[idx]))
  }, character(1))
  return(paste(parts, collapse = "; "))
}

get_pop_window_val <- function (arg0_s, arg1_ymin, arg2_ymax) {
  pop_map <- arg0_s$population
  yrs <- as.numeric(names(pop_map))
  vals <- as.numeric(unlist(pop_map))
  keep <- which(yrs >= arg1_ymin & yrs <= arg2_ymax)
  if (length(keep) == 0) return("-")
  # Take the latest year in the window
  idx <- keep[which.max(yrs[keep])]
  return(fmt_num_eu(vals[idx]))
}
```


# Overview

This supplementary report presents the tabular settlement gazetteer (`pontic_steppe_settlements.json`) accompanying the regional demographic review of the Russian Pontic Steppe (`pontic_steppe_literature_review.pdf`). The JSON dataset contains 176 individual settlements across the Lower Don, Azov Littoral, Taman Peninsula and Kuban-Stavropol Steppe from 10000BC to 1900AD, formatted in the `population_Stadester` structure (keyed by `"<Name>-Russia"`, with `name`, `other_names`, `coords`, `country`, `key` and positive-integer `population` maps).

Population figures are derived from excavated dwelling floor areas and settlement plans for prehistoric and Bronze Age sites [@belanovskaya1995rakushechnyy; @tsybrij2018rakushechnyy; @dolbunova2021rakovinnyye; @skorobogatov2025keramicheskiy; @potapov2013krest; @sharafutdinova1980kobyakovo], walled urban areas and rural surveys for Classical Bosporan, Maeotian and Sarmatian settlements [@paromov1992karta; @kuznetsov2010phanagoria; @garbuzov2011osvoyeniye; @garbuzov2013selskaya; @marchenko2000elizavetovskoye; @kopylov2016vremeni; @batieva2011naseleniye; @vdovchenkov2019meotov], fortress and urban excavations for Khazar Saltovo-Mayaki and Golden Horde cities [@artamonov1958sarkel; @afanasyev1993donskiye; @flyorov2011goroda; @maslovsky2017azak; @maslovsky2022tokhtamysh; @maslovsky2024chislennosti] and Russian *Posolsky Prikaz* rolls, Imperial tax revisions (*revizii*) and the 1897 Census for Don and Kuban Cossack *stanitsas*, peasant *slobodas* and cities [@mininkov1998donskoye; @sushchiy2019institut; @prudnikov2024gorodok; @pronshtein1961zemlya; @kabuzan1976zaseleniye; @agafonov2017zemli; @kolomeytseva2018krestyanstva; @burykina2008osvoyeniye; @parshina2023kubanskogo; @volvenko2023priazovya; @troynitsky1905don; @troynitsky1905kuban].

# Prehistoric, Bronze Age, and Classical Settlements (10000BC - 500AD)

```{r tab-pontic-ancient, results='asis'}
ancient_list <- Filter(function (s) first_year_num(s) <= 500, settlements_list)

df_ancient <- do.call(rbind, lapply(ancient_list, function (s) {
  data.frame(
    Settlement = s$name,
    Other_Names = fmt_other_names(s),
    Coords = fmt_coord_pair(s$coords),
    Span = fmt_span(s),
    Pop_History = fmt_pop_series(s, -10000, 500),
    stringsAsFactors = FALSE
  )
}))

render_table_template(
  df_ancient,
  list(
    caption = "Prehistoric, Bronze Age, Classical Bosporan, Maeotian and Sarmatian settlements of the Russian Pontic Steppe (10000BC - 500AD) compiled from pontic_steppe_settlements.json.",
    label = "tab:pontic-ancient",
    col_names = c("Settlement", "Historical / Alternative Names", "Coords", "Occupied Span", "Population Estimates by Date"),
    scale_to_fit = FALSE
  )
)
```

# Medieval Khazar, Rus, and Golden Horde Settlements (650 - 1550AD)

```{r tab-pontic-medieval, results='asis'}
med_list <- Filter(function (s) {
  yrs <- as.numeric(names(s$population))
  any(yrs >= 650 & yrs <= 1550)
}, settlements_list)

df_med <- do.call(rbind, lapply(med_list, function (s) {
  data.frame(
    Settlement = s$name,
    Other_Names = fmt_other_names(s),
    Coords = fmt_coord_pair(s$coords),
    P_750_850 = get_pop_window_val(s, 750, 850),
    P_900 = get_pop_window_val(s, 880, 920),
    P_1000_1200 = get_pop_window_val(s, 960, 1220),
    P_1340 = get_pop_window_val(s, 1320, 1360),
    P_1380_1500 = get_pop_window_val(s, 1370, 1520),
    stringsAsFactors = FALSE
  )
}))

render_table_template(
  df_med,
  list(
    caption = "Medieval Khazar, Saltovo-Mayaki, Rus and Golden Horde settlements of the Russian Pontic Steppe (650 - 1550AD) compiled from pontic_steppe_settlements.json.",
    label = "tab:pontic-medieval",
    col_names = c("Settlement", "Historical Names", "Coords", "750-850AD", "900AD", "1000-1200AD", "1340AD", "1380-1500AD"),
    scale_to_fit = FALSE
  )
)
```

# Early Modern and Imperial Settlements (1550 - 1900AD)

```{r tab-pontic-modern, results='asis'}
mod_list <- Filter(function (s) {
  yrs <- as.numeric(names(s$population))
  any(yrs >= 1549)
}, settlements_list)

df_mod <- do.call(rbind, lapply(mod_list, function (s) {
  data.frame(
    Settlement = s$name,
    Other_Names = fmt_other_names(s),
    Coords = fmt_coord_pair(s$coords),
    P_1600 = get_pop_window_val(s, 1549, 1650),
    P_1700 = get_pop_window_val(s, 1670, 1720),
    P_1800 = get_pop_window_val(s, 1770, 1820),
    P_1850 = get_pop_window_val(s, 1840, 1865),
    P_1897 = get_pop_window_val(s, 1895, 1898),
    P_1900 = get_pop_window_val(s, 1899, 1905),
    stringsAsFactors = FALSE
  )
}))

render_table_template(
  df_mod,
  list(
    caption = "Cossack gorodki and stanitsas, peasant slobodas, ports and industrial cities of the Russian Pontic Steppe (1550 - 1900AD) compiled from pontic_steppe_settlements.json.",
    label = "tab:pontic-modern",
    col_names = c("Settlement", "Alternative Names", "Coords", "1600AD", "1700AD", "1800AD", "1850AD", "1897AD", "1900AD"),
    scale_to_fit = FALSE
  )
)
```

# Complete Settlement Gazetteer

```{r tab-pontic-all, results='asis'}
df_all <- do.call(rbind, lapply(settlements_list, function (s) {
  data.frame(
    Key = s$key,
    Coords = fmt_coord_pair(s$coords),
    Span = fmt_span(s),
    Pop_Series = fmt_pop_series(s),
    stringsAsFactors = FALSE
  )
}))

render_table_template(
  df_all,
  list(
    caption = "Complete directory of all 176 settlement entries and population series in pontic_steppe_settlements.json (10000BC - 1900AD).",
    label = "tab:pontic-all",
    col_names = c("Stadester Key", "Coords", "Occupied Span", "Complete Population Series (Year: Population)"),
    scale_to_fit = FALSE
  )
)
```

# Sources

::: {#refs}
:::

```{r footnotes, results='asis'}
emit_crd_footnotes()
```
