diff --git a/data/people-groups/india-states.json b/data/people-groups/india-states.json new file mode 100644 index 000000000..c26170d4e --- /dev/null +++ b/data/people-groups/india-states.json @@ -0,0 +1,60 @@ +{ +"meta":{ + "what": "India's 28 states and 8 union territories, each with the UPG-100 people groups located inside it (slugs). The term is \"states and union territories\", never \"provinces\".", + "regenerate": "node plugin-designs/my-upg-100-list/data/emit-india-states.mjs (after pip-stamp.mjs re-stamps admin-membership.json)", + "inputs": { + "membership": { + "path": "plugin-designs/my-upg-100-list/data/admin-membership.json", + "sha384": "sha384-0D8paDuTT3HmH+YQkuuQP0ZIjp84ysz3lIXNaFlNbZKp+OevjnpZjj4FPyi7SJUl" + }, + "geometry": { + "path": "data/boundaries/gb-IND-ADM1-full.geojson", + "sha384": "sha384-0tTsrv3FE4otJ2OGq7IXCs84Y9lHF7rz3cB+Vhxv76ARuw90JtJt7RLAG91L+/9j" + } + }, + "served_geometry": "boundaries/gb-IND-ADM1-full-web.geojson", + "shape_property": "shapeName", + "counts": { + "states": 28, + "union_territories": 8, + "people_groups": 855 + } +}, +"states":[ +{"id":"india-an","iso":"IN-AN","kind":"union_territory","name":"Andaman and Nicobar Islands","shapeName":"Andaman and Nicobar Islands","bbox":[92.204,6.754,93.943,13.673],"count":9,"slugs":["andamanese","bhatu-andamani","brahmin-andamani","coorgi-andamanese","great-andamanese","jarawas","onge","sentinelese","telengi"]}, +{"id":"india-ap","iso":"IN-AP","kind":"state","name":"Andhra Pradesh","shapeName":"Andhra Pradesh","bbox":[76.761,12.624,84.765,19.168],"count":81,"slugs":["accukattavandlu","aguru","aiyarakulu","andhra-vaisnawa","ansari-momin-telugu","arakala","are-bondili","are-katika","atollu","bandollu","bania-agarwal-telegu","bayita-kammara","berad","bhondari","budakukkala","cakketakare","camadollu","cundollu","dammali","dasari","gandalla","gandla","ganta-sayebulu","garadi","gavara","godagali","godari","guddi-eluguwallu","hajjam-telugu","hatgar","jakkula","jandra","jethi","kaikali","karnam","karnibhakthulu","kommu-dasari","konda-kammara","kuliya","kurakula","madiga-dasu","majjula","malis","mandula","manna-dhora","medari","mondivogula","muli","musti-chenchu","nagavamsam","nakhasa","neyyala","ontaris","padigarajulu","pagativeshagallu","paky","pamidi","pamula","pancaratra-srivaisnavas-brahmans","reddi-dora","reddika","runjalavallu","sadaru","sadhu-chetty","sakunapaksollu","samayavallu","senapathi","silavantulu","swakulasali-koshti","tamboli-telugu","thogataveera-kshatriya","tirugatigantla","tolakari","udupula","uppara","vaikhanasa-brahmans","vipravinodulu","yata","yatagiri","yatla","yite"]}, +{"id":"india-ar","iso":"IN-AR","kind":"state","name":"Arunachal Pradesh","shapeName":"Arunāchal Pradesh","bbox":[91.546,26.651,97.411,29.462],"count":7,"slugs":["khamba","khamyang","khowa","momba","na","serdukpen","zakhrin"]}, +{"id":"india-as","iso":"IN-AS","kind":"state","name":"Assam","shapeName":"Assam","bbox":[89.699,24.136,96.018,27.972],"count":15,"slugs":["aiton","bilaspuri","chamar-assamese","hajjam-assamese","kumhar-assamese","man","maria-muslims","matak","mukhi","nai-assamese","namasudra-assamese","sudra-das","taiphake","teli-assamese","turun"]}, +{"id":"india-br","iso":"IN-BR","kind":"state","name":"Bihar","shapeName":"Bihār","bbox":[83.32,24.286,88.3,27.522],"count":18,"slugs":["banpar","camma","dhamin","gadihar","gandharv","hindu-santarash","jains-maithili","jogi-angika","kadar","mukeri","nalband","rajdhop","sahisia","shershabadia","thakurai","tikulhara","tilli","turuk-pasi"]}, +{"id":"india-ch","iso":"IN-CH","kind":"union_territory","name":"Chandigarh","shapeName":"Chandīgarh","bbox":[76.705,30.666,76.849,30.795],"count":1,"slugs":["khatik-punjabi"]}, +{"id":"india-ct","iso":"IN-CT","kind":"state","name":"Chhattisgarh","shapeName":"Chhattīsgarh","bbox":[80.244,17.782,84.396,24.106],"count":24,"slugs":["badhai-chhattisgarhi","bairagi-chhattisgarhi","bargah","beldar-chhattisgarhi","bhangi-chhattisgarhi","bharewa","bharia-bhumia-chhattisgarhi","bhattra-bhatri","bhunjia","chaikwa","dhakad-halbi","dom-chhattisgarhi","ghadwa","gosain-chhattisgarhi","jogi-halbi","kalal-sadri","khairwar-chhattisgarhi","khatik-chhattisgarhi","kol-chhattisgarhi","munnewar","panara","pando","sayyid-chhattisgarhi","son-koli"]}, +{"id":"india-dh","iso":"IN-DH","kind":"union_territory","name":"Dadra and Nagar Haveli and Daman and Diu","shapeName":"Dādra and Nagar Haveli and Damān and Diu","bbox":[70.876,20.047,73.218,20.743],"count":1,"slugs":["sagharias"]}, +{"id":"india-dl","iso":"IN-DL","kind":"union_territory","name":"Delhi","shapeName":"Delhi","bbox":[76.839,28.405,77.347,28.884],"count":1,"slugs":["khatbune"]}, +{"id":"india-ga","iso":"IN-GA","kind":"state","name":"Goa","shapeName":"Goa","bbox":[73.675,14.901,74.336,15.801],"count":5,"slugs":["bania-konkani","gabit","gomantak-maratha","mhalo","mith-gauda"]}, +{"id":"india-gj","iso":"IN-GJ","kind":"state","name":"Gujarat","shapeName":"Gujarāt","bbox":[68.093,20.12,74.477,24.712],"count":90,"slugs":["agaria","agri","anjana-patel-gujarati","bafan","bairagi-gujarati","baloch","bamcha","banjania","barot","barthari","bhodala","borpi","caki","champta","cunara","dafer","dangasiya","dasnami-bawa","depala","dudhwala","gadhai","galiara","garmatan","garvi","gavli","ghantiya","golarana","gosain-gujarati","hajjam-gujarati","halaypotra","hingora","hingorza","jat-muslim","juneja","kahar-gujarati","kamalia","kambodia-bhagat","kapadi","kasar-gujarati","kasbati","kathi","ker","khalpa","khaskeli","khatik-gujarati","langa","machhiyara","majothi","makrani","mandali-muslims","mer","mir","miyana","moghol-gujarati","mujavar","muslim-khatri","muslim-lakhera","muslim-patel","mutwa","narsinghpura","nayak-muslims","node","padhar","panar","paradhi","patanwadia","patni-jamat","patni-vankars","pomla","rathodia","raysipotra","royma","sagar","sandhai-muslims","saranias","sayyid-gujarati","shravan","siddi","sompura-salat","tai","targala","theba","tirgar","turi-barot","vadi","vahivanca-barot","vayati","vyapari","wagheri","wandharo"]}, +{"id":"india-hp","iso":"IN-HP","kind":"state","name":"Himachal Pradesh","shapeName":"Himāchal Pradesh","bbox":[75.594,30.378,79.009,33.257],"count":20,"slugs":["bhojki","daule","dhaki","dosali","gosain-kangri","hadi","hesi","khatik-chambeali","malaneese","masku","nar","nicar","pangwal-buddhists","pangwala","rehar","sanhai","sansoi","seok","swangla","thavi"]}, +{"id":"india-hr","iso":"IN-HR","kind":"state","name":"Haryana","shapeName":"Haryāna","bbox":[74.474,27.653,77.604,30.929],"count":4,"slugs":["arain-punjabi","hajjam-haryanvi","kucband","lakhera-haryanvi"]}, +{"id":"india-jh","iso":"IN-JH","kind":"state","name":"Jharkhand","shapeName":"Jhārkhand","bbox":[83.33,21.97,87.962,25.349],"count":9,"slugs":["dhekaro","jadupetia","jarkhandi-hajjam","jarkhandi-julaha","jharkhandi-nai","modaks","nagbansi","puran","sukiyar"]}, +{"id":"india-jk","iso":"IN-JK","kind":"union_territory","name":"Jammu and Kashmir","shapeName":"Jammu and Kashmīr","bbox":[73.39,32.276,76.78,35.118],"count":33,"slugs":["arain-dogri","bania-dogri","bhand-kashmiri","bomba","budhan","cheintz","chopan","dhobi-dogri","dhyar","domal","galwan","ganz","gardi","gorkun","hajjam-kashmiri","kawoj","kraal","lohar-dogri","manhas","manjygur-muslims","moghol-dogri","nagbansi-dogri","najjar","namdagur","naqqas","pohul","ratal","sayyid-pahari","shaksaz","shina-india","sofi","sudhan","waza"]}, +{"id":"india-ka","iso":"IN-KA","kind":"state","name":"Karnataka","shapeName":"Karnātaka","bbox":[74.086,11.595,78.588,18.459],"count":81,"slugs":["adiyan","agasa","ansari-momin-kannada","arasu","ayri","bagga","baira","balasantoshi","bandi","bania-kannada","batada","bayil-kammara","bayil-pattar","bellara","bestha","chaptegar","charodi","chenna-dasar","dombi-dasa","durga-murga","galada-kokani","ganti-chores","gatti","gauli","ghadi","godda","gond-kannada","gudikara","guraw","hajjam-kannada","halleer","harkantra","hasalaru","helawa","holeya-dasari","jatigar","jaweri","jogi-telugu","jogi-tulu","khakar","kolari","komara-panta","koosa","koraga-korra","kottari","koyava","kudiya","kudubi","kummara-kumbara","kurahina-setti","ladar","maila","malaikudi","maleru","manser","meda","mogaveera","mundala","nadors","nai-kannada","nairy","natua","nawaiyat","padarthis","padiyar","pambada","pattegar","pendar","pinjara-kannada","rachavar","raddi","rajapuri","raneyar","raval","raya-rawat","sapalyas","sayyid-kannada","soligaru","sudir","thammadi","vilyagaras"]}, +{"id":"india-kl","iso":"IN-KL","kind":"state","name":"Kerala","shapeName":"Kerala","bbox":[74.868,8.292,77.412,12.796],"count":72,"slugs":["allar","ambalavasi","anthur-nair","arayavati","ayyanavar","bakuda","bania-malayalam","bowis-mogeyar","cakkala","cakkyar-nambiyar","chakkiyar","chamar-muchi-malayalam","chengazhi-nambiar","cingatanmar","dheevara","erawallan","ezhavathi","ilayatu","kakkalan","kalanadies","kalladi","kallattu-kurup","karimpalan","kavara","kavuthiyan","kidaran","kocu-wellan","kollan","kootan","kumar-kshatriya","kummara-kusuvan","kunduvadian","kurukkal","malai-pandaram","malayalar","maran-nairs","marasari","mavilan","moger-malayalam","moothan","mukkari","musari","muttatu","nalkadaya","nambutiris","narsanna","nasuvan","nayadi","otans","padannan","palakad-tandans","pandithar","pathinaickans","pathiyan","pisaroti","poduval","pulisari","puspakan-unni","reddi","samanthan","thachanad-moopan","tiyattunni","urindavan","vadugan","valinjiyar","vetan","vilkurup","visavan","wainadan-cetti","wainadan-kader","wainadan-pulayan","wayanad-pathiyan"]}, +{"id":"india-la","iso":"IN-LA","kind":"union_territory","name":"Ladakh","shapeName":"Ladākh","bbox":[72.529,32.338,80.327,37.077],"count":6,"slugs":["bania-kashmiri","brukpa","campa","dokhpa","mon-india","purig-pa"]}, +{"id":"india-ld","iso":"IN-LD","kind":"union_territory","name":"Lakshadweep","shapeName":"Lakshadweep","bbox":[72.165,8.264,73.702,11.704],"count":1,"slugs":["laccadive"]}, +{"id":"india-mh","iso":"IN-MH","kind":"state","name":"Maharashtra","shapeName":"Mahārāshtra","bbox":[72.655,15.606,80.898,22.034],"count":54,"slugs":["attar-saibulu","baharupi","barda","bari-marathi","bharwad","bhoi-marathi","chari","chitari","citrakathi","dhawad","dom-marathi","garodi","garpagari","gondhali","gopal","hardas","hatkar","jhadi","kachchi-kushwaha","kaghzi","kapewar","kasikapadi","kathar","kharvi","khatik-marathi","kokaniz","kolhati","kudimahan","ladsi","lajjhar","lodha-marathi","lonar","mana","mitna","mondiwar","muslim-kachera","nahal","naikpod","nandiwala","neeli","nilkanth","pangul","phularis","pinjara-marathi","pochammalollu","prabhu-patane","sahi","sanghar","sarodi","sutar-mistry","takari","velip","waidu","yelmar"]}, +{"id":"india-ml","iso":"IN-ML","kind":"state","name":"Meghalaya","shapeName":"Meghālaya","bbox":[89.814,25.029,92.803,26.12],"count":2,"slugs":["banai","dalu-india"]}, +{"id":"india-mn","iso":"IN-MN","kind":"state","name":"Manipur","shapeName":"Manipur","bbox":[92.971,23.833,94.745,25.692],"count":5,"slugs":["koiren","lois","pangans","thanga","yatibi"]}, +{"id":"india-mp","iso":"IN-MP","kind":"state","name":"Madhya Pradesh","shapeName":"Madhya Pradesh","bbox":[74.03,21.07,82.81,26.869],"count":33,"slugs":["anjana","banchada","baraua","bari-hindi","basudeva","berar","bhanumati","bhurtia","dhed","gachha","hajjam-hindi","jadam","jains-hindi","kachera-bagheli","kandera","kasar-hindi","ladia","lahangir","manbhav","mankar","muskhan","muslim-gond","naita","namdeo","naoda","nat-bagheli","nema","rujjhar","sais","sikligar","sisgars","soria","zamral"]}, +{"id":"india-mz","iso":"IN-MZ","kind":"state","name":"Mizoram","shapeName":"Mizoram","bbox":[92.257,21.941,93.436,24.521],"count":0,"slugs":[]}, +{"id":"india-nl","iso":"IN-NL","kind":"state","name":"Nagaland","shapeName":"Nāgāland","bbox":[93.327,25.199,95.242,27.036],"count":0,"slugs":[]}, +{"id":"india-or","iso":"IN-OR","kind":"state","name":"Odisha","shapeName":"Odisha","bbox":[81.388,17.813,87.486,22.568],"count":64,"slugs":["baha-i","bairagi-odia","bangti","banka","bari-odia","bauti","bavuri","bhoi-odia","bondo","brahman-jyoti","candhai-maru","chatarkheya","cherua","citrakar","daitapati","damla","darji-odia","dhera","didayi","eneti-dora","ghantarghada","ghogia","ghusuria","godra","guna","irika","janughanta","jogi-odia","kachchi-alia","kamila","kapasiya","kasar-odia","khadura","khandwal","khati-odia","kobari","koraga","kuda","kuli","kurunga","kusta","liari","maghia","mahuria","malia","markande-kanjia","mundapotta","nai-odia","naksia","noliya","oriya","panka-odia","parenga","radhi","rana","sabakhia","samasi","sanei","sayyid-odia","sinduria","suda","thanapati","tharua","tula-vina"]}, +{"id":"india-pb","iso":"IN-PB","kind":"state","name":"Punjab","shapeName":"Punjab","bbox":[73.88,29.542,76.939,32.513],"count":4,"slugs":["daoli","gagra","pinjara-punjabi","racband"]}, +{"id":"india-py","iso":"IN-PY","kind":"union_territory","name":"Puducherry","shapeName":"Puducherry","bbox":[75.527,10.827,82.344,16.761],"count":5,"slugs":["indo-french","indo-vietnamese","sonavar","vettalaikarans","vettiyan"]}, +{"id":"india-rj","iso":"IN-RJ","kind":"state","name":"Rajasthan","shapeName":"Rājasthān","bbox":[69.484,23.061,78.272,30.198],"count":48,"slugs":["badi-marwari","baret","bargi","bhopa","bhutta","cejara","chadwa","chheepi","chobdar","chungar","cudigar","dadupanthi","damami","dhola","ghihara","gosain-rajasthani","gurru","hiranbaj","khadim","kharol","kriyagar","laskari","mangnyar-muslims","moghol-marwari","mogia","murhid","nagda","nai-mewari","nyaria","pajabgar","pakhimara","rajkumhar","rajpurohit","rath","runwalas","sain","sanchia","santia","saraogi","shikari","singiwala","sorgars","sunga","tak","usta","van-baoriya","ved","vijabargiya"]}, +{"id":"india-sk","iso":"IN-SK","kind":"state","name":"Sikkim","shapeName":"Sikkim","bbox":[88.014,27.079,88.92,28.129],"count":1,"slugs":["yakha"]}, +{"id":"india-tg","iso":"IN-TG","kind":"state","name":"Telangana","shapeName":"Telangāna","bbox":[77.236,15.836,81.323,19.916],"count":26,"slugs":["are-kapu","attagara","bhatrajulu","bindla","bolla","cippollu","gangireddula","gosangi","gurika","jains-telugu","kachi","katipamula","khatik-telugu","lakkamari-kapu","mala-masti","matangi","mitha-ayyalavar","moghal-telugu","muchi-chamar","pambala","pariki-muggula","patkars","rajannalu","saradagallu","sindhollu","verra-musti"]}, +{"id":"india-tn","iso":"IN-TN","kind":"state","name":"Tamil Nadu","shapeName":"Tamil Nādu","bbox":[76.233,8.077,80.347,13.565],"count":64,"slugs":["alavan","ansari-momin-tamil","anuppan","aranadan","baig","bondili","cekkala","chamar-muchi-tamil","chayakkaran","dasa-palanjika","dayare","dhobi-tamil","gangawar","gudugudupandi","gurukkal","jains-tamil","jati-pillai","kambalattan","kambar","kannada-naicker","kannada-saineegar","karaiyar","karlar","karna-sale","katesar","kattiakaran","kayalar","kepmari","koliyan","kongu-vaisnawa","kongu-vellala-gounder","kota","krishnavaka","kudumi","kuluvan","kummara-kummari","kunnuva-vellalar","malai-vellala","maleyan","maniyattikkaran","mudugar","nakkala","namdeo-maratha","nanjil-mudali","pannadi","panniandi","parkavakulam","parvatarajakulam","pattanavan","perumkollar","salivahanam","savalakkarar","sayyid-tamil","semman","senaithalaivar","siviar","tambiran","tholuva-naicker","thoraiyar","tondman","uppillian","urikkar-naicker","vellaiyan-kuppam-padaiyaci","vettaikkaran"]}, +{"id":"india-tr","iso":"IN-TR","kind":"state","name":"Tripura","shapeName":"Tripura","bbox":[91.15,22.939,92.332,24.528],"count":11,"slugs":["bajandar","banik-bania","barua","haluadas","kanda","mahisyadas","malakar","sabar","saha","shabdakar","tripuri-namasudra"]}, +{"id":"india-up","iso":"IN-UP","kind":"state","name":"Uttar Pradesh","shapeName":"Uttar Pradesh","bbox":[77.084,23.87,84.635,30.406],"count":23,"slugs":["ahmadiya","arain-kannauji","badhik","badi","balahar","bargala","bargujar","bhuiyar","bisati","charwaha","gadhia","habura","hijra","kankali","khangar-bundeli","mujavir","pankhiya","patari","putliwale","rai-bhat-muslims","rastogi","rave","sheikhzade"]}, +{"id":"india-ut","iso":"IN-UT","kind":"state","name":"Uttarakhand","shapeName":"Uttarākhand","bbox":[77.573,28.723,81.045,31.456],"count":7,"slugs":["anwal","chaudangsi","jimdar","kuthliya-bora","raji-india","sayyid-garhwali","tagewale"]}, +{"id":"india-wb","iso":"IN-WB","kind":"state","name":"West Bengal","shapeName":"West Bengal","bbox":[85.82,21.484,89.874,27.221],"count":30,"slugs":["angarok","ansari-momin-bengali","bagti","baida","baisya","bhakta","bhalla","bhujel","byadh","chunari","dhal-kstriya","dhimal","ghazi","hajjam-bengali","jains-bengali","kakmara","kapuria","kasar-bengali","khairwar-bengali","khalifa","khatik-bengali","kurol","mirshikar","paharia","pinjara-dhunia-bengali","siyalgar","sunuwar","sutradhar","tamboli-bengali","tripuri-kumhar"]} +]} diff --git a/internal/check-india-states.mjs b/internal/check-india-states.mjs new file mode 100644 index 000000000..1ce890d0e --- /dev/null +++ b/internal/check-india-states.mjs @@ -0,0 +1,108 @@ +#!/usr/bin/env node +// check-india-states — India's states-and-union-territories table (data/people-groups/india-states.json) stays +// TRUE to the stamping it is derived from, and the served copy is the pinned one. +// +// WHY: UPG-100's batch selection finds a state's people groups in it, and Country Posters builds one poster per +// state / union territory from it. When the people-group data changes, the stamping is redone +// (pip-stamp.mjs → admin-membership.json) and this table must follow; a table that silently kept the old +// membership would print confident, wrong posters. Headless, no network. Asserts: +// A. SHAPE: all 36 divisions of ISO 3166-2:IN, 28 'state' + 8 'union_territory', unique iso and id +// (india-), each with a sane bbox inside India, count == slugs.length; Nagaland + Mizoram present +// even with no people groups (each consumer decides what an empty division shows). +// B. MEMBERSHIP: the union of every division's slugs == admin-membership.json's India slugs (855 today), and +// every slug sits in exactly ONE division. +// C. SHAPES MATCH: every shapeName is a feature of the served geometry (and of the full geometry it is cut from), +// so a poster can highlight its shape by the exact property. +// D. NO DRIFT: re-deriving the table from today's inputs gives the committed bytes, and the input sha384s +// recorded in meta are the files on disk — a changed stamping without a re-emit FAILS here. +// E. PIN: INDIA_STATES_SHA384 == sha384(the committed bytes) == sha384(the served copy in the tree); size ceiling. +import { readFileSync, existsSync } from 'node:fs' +import { createHash } from 'node:crypto' +import { resolve, dirname, join } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const P = (...a) => join(REPO, ...a) +const fails = [] +const notes = [] +const ok = (cond, msg, note) => { if (!cond) fails.push(msg); else if (note) notes.push(` ok ${note}`) } +const sha = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64') + +const TABLE = P('data/people-groups/india-states.json') +const SERVED = P('public/dx-maps/assets/people-groups/india-states.json') +const SIZE_CEILING = 24 * 1024 +let raw, table, membership, emitter, pin, full, web +try { + raw = readFileSync(TABLE) + table = JSON.parse(raw) + membership = JSON.parse(readFileSync(P('plugin-designs/my-upg-100-list/data/admin-membership.json'), 'utf8')) + emitter = await import(pathToFileURL(P('plugin-designs/my-upg-100-list/data/emit-india-states.mjs')).href) + ;({ INDIA_STATES_SHA384: pin } = await import(pathToFileURL(P('library/data/indiaStates.integrity.js')).href)) + full = JSON.parse(readFileSync(P('data/boundaries/gb-IND-ADM1-full.geojson'), 'utf8')) + web = JSON.parse(readFileSync(P('data', table.meta.served_geometry), 'utf8')) +} catch (e) { + console.error(`INDIA-STATES: NOT MEASURED (exit 75) — an input did not load: ${e.message}`) + process.exit(75) +} +const states = table.states || [] + +// ── A. shape ───────────────────────────────────────────────────────────────────────────────────── +const isos = states.map((s) => s.iso), ids = states.map((s) => s.id) +const kinds = { state: 0, union_territory: 0 } +for (const s of states) kinds[s.kind] = (kinds[s.kind] || 0) + 1 +ok(states.length === 36 && new Set(isos).size === 36 && isos.every((i) => emitter.IN_DIVISIONS[i]), + `the table has ${states.length} divisions (${new Set(isos).size} distinct ISO codes); ISO 3166-2:IN has 36`) +ok(kinds.state === 28 && kinds.union_territory === 8 && Object.keys(kinds).length === 2, + `kinds are ${JSON.stringify(kinds)}, expected 28 state + 8 union_territory`, 'all 36 divisions: 28 states + 8 union territories, unique ISO codes') +ok(new Set(ids).size === 36 && states.every((s) => s.id === 'india-' + s.iso.slice(3).toLowerCase()), + 'ids are not the unique india- form', 'ids are india-, unique (wall cards, packet file names)') +const badBox = states.filter((s) => !(Array.isArray(s.bbox) && s.bbox.length === 4 && s.bbox[0] < s.bbox[2] && s.bbox[1] < s.bbox[3] + && s.bbox[0] >= 67 && s.bbox[2] <= 98 && s.bbox[1] >= 6 && s.bbox[3] <= 38)).map((s) => s.iso) +ok(!badBox.length, `bboxes out of shape or outside India: ${badBox.join(', ')}`) +const badCount = states.filter((s) => !Array.isArray(s.slugs) || s.count !== s.slugs.length).map((s) => s.iso) +ok(!badCount.length, `count != slugs.length for ${badCount.join(', ')}`, 'every bbox sits inside India; every count is its slugs\' length') +ok(['IN-NL', 'IN-MZ'].every((i) => isos.includes(i)), 'Nagaland or Mizoram is missing: an empty division must still be listed') + +// ── B. membership ──────────────────────────────────────────────────────────────────────────────── +const india = Object.entries(membership).filter(([, m]) => m && m.country === 'IND').map(([s]) => s).sort() +const seen = new Map() +for (const s of states) for (const slug of s.slugs || []) seen.set(slug, (seen.get(slug) || 0) + 1) +const twice = [...seen].filter(([, n]) => n > 1).map(([s]) => s) +const missing = india.filter((s) => !seen.has(s)), extra = [...seen.keys()].filter((s) => !india.includes(s)) +ok(!twice.length, `slugs in more than one division: ${twice.slice(0, 6).join(', ')}`) +ok(!missing.length && !extra.length, + `membership differs from admin-membership.json: missing ${missing.slice(0, 5).join(', ') || '-'}; extra ${extra.slice(0, 5).join(', ') || '-'}`, + `the ${india.length} India people groups, each in exactly one division (= admin-membership.json)`) + +// ── C. shapes match the geometry ───────────────────────────────────────────────────────────────── +const webNames = new Set(web.features.map((f) => f.properties[table.meta.shape_property])) +const fullNames = new Set(full.features.map((f) => f.properties.shapeName)) +const noShape = states.filter((s) => !webNames.has(s.shapeName) || !fullNames.has(s.shapeName)).map((s) => s.shapeName) +ok(!noShape.length, `shapeNames not in ${table.meta.served_geometry}: ${noShape.join(', ')}`, + `every shapeName is a feature of ${table.meta.served_geometry}`) + +// ── D. no drift ────────────────────────────────────────────────────────────────────────────────── +for (const [k, inp] of Object.entries(table.meta.inputs || {})) { + const now = existsSync(P(inp.path)) ? sha(readFileSync(P(inp.path))) : 'MISSING' + ok(now === inp.sha384, `meta.inputs.${k} (${inp.path}) changed since the table was emitted: re-run node plugin-designs/my-upg-100-list/data/emit-india-states.mjs`) +} +let rebuilt = null +try { rebuilt = emitter.buildIndiaStates().text } catch (e) { fails.push(`re-deriving the table threw: ${e.message}`) } +ok(rebuilt === raw.toString('utf8'), 'the committed table is not what the emitter derives from today\'s inputs: re-run the emitter', + 'NO DRIFT: re-derived from today\'s inputs it is byte-identical, and meta\'s input sha384s are the files on disk') + +// ── E. pin + served copy ───────────────────────────────────────────────────────────────────────── +ok(pin === sha(raw), `INDIA_STATES_SHA384 (${String(pin).slice(0, 20)}…) is not the table's sha384 (${sha(raw).slice(0, 20)}…): re-run the emitter`) +ok(existsSync(SERVED) && sha(readFileSync(SERVED)) === pin, existsSync(SERVED) + ? 'the served copy (public/dx-maps/assets/people-groups/india-states.json) is not the pinned table: run bun run tree' + : 'the table is not in the tree (public/dx-maps/assets/people-groups/india-states.json): add it to generate-tree\'s allowlist and run bun run tree', + 'pin == the table == the served copy') +ok(raw.length <= SIZE_CEILING, `the table is ${raw.length} B, over the ${SIZE_CEILING} B ceiling`, `${raw.length} B (ceiling ${SIZE_CEILING})`) + +for (const n of notes) console.log(n) +if (fails.length) { + console.error('INDIA-STATES: FAIL') + for (const f of fails) console.error(` ✗ ${f}`) + process.exit(1) +} +console.log('INDIA-STATES: PASS — 36 divisions, every India people group in exactly one, shapes match the served geometry, no drift from the stamping, and the served copy is the pinned one.') diff --git a/internal/generate-tree.mjs b/internal/generate-tree.mjs index 60529df7f..160893553 100644 --- a/internal/generate-tree.mjs +++ b/internal/generate-tree.mjs @@ -1954,6 +1954,10 @@ function main() { // people-groups — all-people-groups.vue (the slim JSON only; NOT the 16 MB CSV, // which no element fetches and which dropin.exclude already withholds). 'people-groups/people-groups-slim.json', + // …and India's states and union territories with their people groups (data/people-groups/india-states.json), + // fetched by library/data/indiaStates.js only when a state is needed (UPG-100's batch selection, Country + // Posters' India section). sha384-pinned in library/data/indiaStates.integrity.js. + 'people-groups/india-states.json', // …and its compact cut (build-points.mjs): what all-people-groups actually fetches, 0.74 MB. 'people-groups/people-groups-points.json', // pplr-data-maps — fetched through dataUrl() (src/utils/pplrData.js). pg-names.json loads diff --git a/library/data/indiaStates.integrity.js b/library/data/indiaStates.integrity.js new file mode 100644 index 000000000..f7ec51410 --- /dev/null +++ b/library/data/indiaStates.integrity.js @@ -0,0 +1,4 @@ +// GENERATED by plugin-designs/my-upg-100-list/data/emit-india-states.mjs, in the same run that writes +// data/people-groups/india-states.json: the sha384 of its exact bytes. library/data/indiaStates.js refuses a served +// copy that does not match. Do not edit by hand; re-run the emitter. +export const INDIA_STATES_SHA384 = 'sha384-PC2ve+U+IHimP8b4GBPS7LpbXqQKFzxtS8fs6I4pXBP7OqXOAqBx7kiWG0FWjxL6' diff --git a/library/data/indiaStates.js b/library/data/indiaStates.js new file mode 100644 index 000000000..78cf38693 --- /dev/null +++ b/library/data/indiaStates.js @@ -0,0 +1,52 @@ +// indiaStates.js — India's 28 states and 8 union territories, each with the people groups located inside it. +// +// SERVED, NOT BUNDLED: the table (data/people-groups/india-states.json, written by +// plugin-designs/my-upg-100-list/data/emit-india-states.mjs) ships as assets/people-groups/india-states.json and is +// fetched here only when a state is actually needed (a state in UPG-100's batch selection, Country Posters' India +// section). This build inlines every import(), so a bundled table would ride every page load. +// +// Same shape as library/packet/packetStrings.js: the copy must match the sha384 pinned in the SAME emitter run +// (indiaStates.integrity.js), one promise per URL, and a failure is dropped from the cache so a later call +// fetches again instead of inheriting one bad moment. It REJECTS on failure: there is no honest fallback for +// "which people groups live in this state". +// +// const t = await loadIndiaStates(assetsBase) +// t.states → [{ id:'india-gj', iso:'IN-GJ', kind:'state'|'union_territory', name, shapeName, bbox, count, slugs }] +// t.byShapeName → Map(shapeName → entry) (the map's ADM1 features carry shapeName) +// t.byIso → Map(iso → entry) +import { INDIA_STATES_SHA384 } from './indiaStates.integrity.js' +import { sha384 } from '../packet/sha384.js' + +const PAGE_BASE = globalThis.document?.baseURI || undefined +const cache = new Map() + +export function indiaStatesUrl(assetsBase) { + return new URL(`${assetsBase}/people-groups/india-states.json`, PAGE_BASE).href +} + +export function loadIndiaStates(assetsBase) { + if (!assetsBase) return Promise.reject(new Error('no assets base to fetch india-states.json from')) + let url + try { url = indiaStatesUrl(assetsBase) } catch (e) { return Promise.reject(e) } + if (!cache.has(url)) { + const p = (async () => { + const res = await fetch(url, { cache: 'force-cache' }) + if (!res.ok) throw new Error(`india-states.json not served (${res.status})`) + const text = await res.text() + if ((await sha384(text)) !== INDIA_STATES_SHA384) throw new Error('india-states.json failed its integrity check') + const data = JSON.parse(text) + const states = Array.isArray(data.states) ? data.states : [] + return { + meta: data.meta || {}, + states, + byShapeName: new Map(states.map((s) => [s.shapeName, s])), + byIso: new Map(states.map((s) => [s.iso, s])), + } + })() + p.catch(() => { if (cache.get(url) === p) cache.delete(url) }) + cache.set(url, p) + } + return cache.get(url) +} + +export default { loadIndiaStates, indiaStatesUrl } diff --git a/plugin-designs/my-upg-100-list/data/emit-india-states.mjs b/plugin-designs/my-upg-100-list/data/emit-india-states.mjs new file mode 100644 index 000000000..178f84e93 --- /dev/null +++ b/plugin-designs/my-upg-100-list/data/emit-india-states.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node +// emit-india-states.mjs — India's states and union territories, each with the people groups inside it. +// +// node plugin-designs/my-upg-100-list/data/emit-india-states.mjs +// +// ONE table, two consumers: UPG-100's batch selection (a state selection finds its people groups) and Country +// Posters' India section (one poster per state / union territory). It is SERVED, not bundled: the tree ships it +// as assets/people-groups/india-states.json and library/data/indiaStates.js fetches it only when a state is +// needed, checked against the sha384 pin this script writes in the SAME run (so a re-emit can never leave a +// stale pin behind). +// +// DERIVED, NOT RE-STAMPED. The membership comes from the committed admin-membership.json (pip-stamp.mjs: a +// point-in-polygon of every live record's coordinates against the ADM1 shapes, with a nearest-centroid fallback +// for border slivers). Re-stamping here would re-place all 2,106 records from today's data and move the counts +// baked from that file; deriving keeps the two in step, and check:india-states fails if they ever drift (it +// compares the input sha384s recorded in meta with the files on disk). +// +// The ids, codes, kinds and English names are fixed by ISO 3166-2:IN (28 states + 8 union territories). The +// shape names come from the geometry, so a poster can highlight its shape by the exact feature property. +import { readFileSync, writeFileSync } from 'node:fs' +import { createHash } from 'node:crypto' +import { dirname, join, resolve, relative } from 'node:path' +import { fileURLToPath } from 'node:url' + +const HERE = dirname(fileURLToPath(import.meta.url)) +const REPO = resolve(HERE, '..', '..', '..') +const MEMBERSHIP = join(HERE, 'admin-membership.json') +const GEOMETRY = join(REPO, 'data', 'boundaries', 'gb-IND-ADM1-full.geojson') +const SERVED_GEOMETRY = 'boundaries/gb-IND-ADM1-full-web.geojson' // what a poster fetches; the same 36 shapeNames +const OUT = join(REPO, 'data', 'people-groups', 'india-states.json') +const PIN = join(REPO, 'library', 'data', 'indiaStates.integrity.js') + +// ISO 3166-2:IN — code → [English name, kind] +export const IN_DIVISIONS = { + 'IN-AN': ['Andaman and Nicobar Islands', 'union_territory'], 'IN-AP': ['Andhra Pradesh', 'state'], + 'IN-AR': ['Arunachal Pradesh', 'state'], 'IN-AS': ['Assam', 'state'], 'IN-BR': ['Bihar', 'state'], + 'IN-CH': ['Chandigarh', 'union_territory'], 'IN-CT': ['Chhattisgarh', 'state'], + 'IN-DH': ['Dadra and Nagar Haveli and Daman and Diu', 'union_territory'], 'IN-DL': ['Delhi', 'union_territory'], + 'IN-GA': ['Goa', 'state'], 'IN-GJ': ['Gujarat', 'state'], 'IN-HP': ['Himachal Pradesh', 'state'], + 'IN-HR': ['Haryana', 'state'], 'IN-JH': ['Jharkhand', 'state'], 'IN-JK': ['Jammu and Kashmir', 'union_territory'], + 'IN-KA': ['Karnataka', 'state'], 'IN-KL': ['Kerala', 'state'], 'IN-LA': ['Ladakh', 'union_territory'], + 'IN-LD': ['Lakshadweep', 'union_territory'], 'IN-MH': ['Maharashtra', 'state'], 'IN-ML': ['Meghalaya', 'state'], + 'IN-MN': ['Manipur', 'state'], 'IN-MP': ['Madhya Pradesh', 'state'], 'IN-MZ': ['Mizoram', 'state'], + 'IN-NL': ['Nagaland', 'state'], 'IN-OR': ['Odisha', 'state'], 'IN-PB': ['Punjab', 'state'], + 'IN-PY': ['Puducherry', 'union_territory'], 'IN-RJ': ['Rajasthan', 'state'], 'IN-SK': ['Sikkim', 'state'], + 'IN-TG': ['Telangana', 'state'], 'IN-TN': ['Tamil Nadu', 'state'], 'IN-TR': ['Tripura', 'state'], + 'IN-UP': ['Uttar Pradesh', 'state'], 'IN-UT': ['Uttarakhand', 'state'], 'IN-WB': ['West Bengal', 'state'], +} + +const sha = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64') +const rel = (p) => relative(REPO, p).split('\\').join('/') + +export function buildIndiaStates() { + const membershipRaw = readFileSync(MEMBERSHIP) + const geometryRaw = readFileSync(GEOMETRY) + const membership = JSON.parse(membershipRaw) + const features = JSON.parse(geometryRaw).features + const bySlugState = new Map() + for (const [slug, m] of Object.entries(membership)) if (m && m.country === 'IND') bySlugState.set(slug, m.admin1) + const bbox = (geom) => { + let w = Infinity, s = Infinity, e = -Infinity, n = -Infinity + const walk = (c) => { if (typeof c[0] === 'number') { w = Math.min(w, c[0]); e = Math.max(e, c[0]); s = Math.min(s, c[1]); n = Math.max(n, c[1]) } else for (const x of c) walk(x) } + walk(geom.coordinates) + return [w, s, e, n].map((v) => Math.round(v * 1000) / 1000) + } + const states = features + .map((f) => ({ iso: f.properties.shapeISO, shapeName: f.properties.shapeName, geom: f.geometry })) + .sort((a, b) => a.iso.localeCompare(b.iso)) + .map(({ iso, shapeName, geom }) => { + const def = IN_DIVISIONS[iso] + if (!def) throw new Error(`the geometry has ${iso} (${shapeName}), which is not an ISO 3166-2:IN division`) + const slugs = [...bySlugState].filter(([, st]) => st === shapeName).map(([s]) => s).sort() + return { id: 'india-' + iso.slice(3).toLowerCase(), iso, kind: def[1], name: def[0], shapeName, bbox: bbox(geom), count: slugs.length, slugs } + }) + const meta = { + what: 'India\'s 28 states and 8 union territories, each with the UPG-100 people groups located inside it (slugs). The term is "states and union territories", never "provinces".', + regenerate: 'node plugin-designs/my-upg-100-list/data/emit-india-states.mjs (after pip-stamp.mjs re-stamps admin-membership.json)', + inputs: { + membership: { path: rel(MEMBERSHIP), sha384: sha(membershipRaw) }, + geometry: { path: rel(GEOMETRY), sha384: sha(geometryRaw) }, + }, + served_geometry: SERVED_GEOMETRY, + shape_property: 'shapeName', + counts: { states: states.filter((s) => s.kind === 'state').length, union_territories: states.filter((s) => s.kind === 'union_territory').length, + people_groups: states.reduce((a, s) => a + s.count, 0) }, + } + // One division per line: small enough to review as a diff, and still one fetch of ~17 KB. + const text = '{\n"meta":' + JSON.stringify(meta, null, 1) + ',\n"states":[\n' + + states.map((s) => JSON.stringify(s)).join(',\n') + '\n]}\n' + return { text, meta, states } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const { text, meta } = buildIndiaStates() + writeFileSync(OUT, text) + const pin = sha(Buffer.from(text, 'utf8')) + writeFileSync(PIN, '// GENERATED by plugin-designs/my-upg-100-list/data/emit-india-states.mjs, in the same run that writes\n' + + '// data/people-groups/india-states.json: the sha384 of its exact bytes. library/data/indiaStates.js refuses a served\n' + + '// copy that does not match. Do not edit by hand; re-run the emitter.\n' + + `export const INDIA_STATES_SHA384 = '${pin}'\n`) + console.log(`emit-india-states: ${meta.counts.states} states + ${meta.counts.union_territories} union territories, ${meta.counts.people_groups} people groups`) + console.log(` wrote ${rel(OUT)} (${Buffer.byteLength(text)} B) + ${rel(PIN)} (${pin.slice(0, 20)}…)`) +} diff --git a/plugin-designs/my-upg-100-list/data/pip-stamp.mjs b/plugin-designs/my-upg-100-list/data/pip-stamp.mjs index 4ad79f02a..b53266519 100644 --- a/plugin-designs/my-upg-100-list/data/pip-stamp.mjs +++ b/plugin-designs/my-upg-100-list/data/pip-stamp.mjs @@ -89,7 +89,10 @@ function loadAdm1(file) { })); } -const IND_POLYS = loadAdm1("gb-IND-ADM1.geojson"); +// India is stamped against ALL 36 states and union territories (data/boundaries/gb-IND-ADM1-full.geojson). The +// 34-shape map copy beside this file has no Nāgāland or Mizoram, so a group located there would have been +// stamped into a neighbour by the fallback (none is today: re-stamped 2026-09-30, 853/855 agree, 0 differ). +const IND_POLYS = loadAdm1("../../../data/boundaries/gb-IND-ADM1-full.geojson"); const CHN_POLYS = loadAdm1("gb-CHN-ADM1.geojson"); // Stamp one point against a polygon set. Returns { shapeName, fallback } @@ -186,3 +189,4 @@ console.log(` records: ${records.length}`); console.log(` IND located: ${indCountedTotal} CHN located: ${chnCountedTotal}`); console.log(` real-stamp: ${stampedReal} fallback: ${fallbackCount}`); console.log(` wrote admin-membership.json + admin-counts.json`); +console.log(" NEXT: node plugin-designs/my-upg-100-list/data/emit-india-states.mjs (re-derives data/people-groups/india-states.json + its pin; check:india-states fails until you do)"); diff --git a/public/dx-maps/assets/people-groups/india-states.json b/public/dx-maps/assets/people-groups/india-states.json new file mode 100644 index 000000000..c26170d4e --- /dev/null +++ b/public/dx-maps/assets/people-groups/india-states.json @@ -0,0 +1,60 @@ +{ +"meta":{ + "what": "India's 28 states and 8 union territories, each with the UPG-100 people groups located inside it (slugs). The term is \"states and union territories\", never \"provinces\".", + "regenerate": "node plugin-designs/my-upg-100-list/data/emit-india-states.mjs (after pip-stamp.mjs re-stamps admin-membership.json)", + "inputs": { + "membership": { + "path": "plugin-designs/my-upg-100-list/data/admin-membership.json", + "sha384": "sha384-0D8paDuTT3HmH+YQkuuQP0ZIjp84ysz3lIXNaFlNbZKp+OevjnpZjj4FPyi7SJUl" + }, + "geometry": { + "path": "data/boundaries/gb-IND-ADM1-full.geojson", + "sha384": "sha384-0tTsrv3FE4otJ2OGq7IXCs84Y9lHF7rz3cB+Vhxv76ARuw90JtJt7RLAG91L+/9j" + } + }, + "served_geometry": "boundaries/gb-IND-ADM1-full-web.geojson", + "shape_property": "shapeName", + "counts": { + "states": 28, + "union_territories": 8, + "people_groups": 855 + } +}, +"states":[ +{"id":"india-an","iso":"IN-AN","kind":"union_territory","name":"Andaman and Nicobar Islands","shapeName":"Andaman and Nicobar Islands","bbox":[92.204,6.754,93.943,13.673],"count":9,"slugs":["andamanese","bhatu-andamani","brahmin-andamani","coorgi-andamanese","great-andamanese","jarawas","onge","sentinelese","telengi"]}, +{"id":"india-ap","iso":"IN-AP","kind":"state","name":"Andhra Pradesh","shapeName":"Andhra Pradesh","bbox":[76.761,12.624,84.765,19.168],"count":81,"slugs":["accukattavandlu","aguru","aiyarakulu","andhra-vaisnawa","ansari-momin-telugu","arakala","are-bondili","are-katika","atollu","bandollu","bania-agarwal-telegu","bayita-kammara","berad","bhondari","budakukkala","cakketakare","camadollu","cundollu","dammali","dasari","gandalla","gandla","ganta-sayebulu","garadi","gavara","godagali","godari","guddi-eluguwallu","hajjam-telugu","hatgar","jakkula","jandra","jethi","kaikali","karnam","karnibhakthulu","kommu-dasari","konda-kammara","kuliya","kurakula","madiga-dasu","majjula","malis","mandula","manna-dhora","medari","mondivogula","muli","musti-chenchu","nagavamsam","nakhasa","neyyala","ontaris","padigarajulu","pagativeshagallu","paky","pamidi","pamula","pancaratra-srivaisnavas-brahmans","reddi-dora","reddika","runjalavallu","sadaru","sadhu-chetty","sakunapaksollu","samayavallu","senapathi","silavantulu","swakulasali-koshti","tamboli-telugu","thogataveera-kshatriya","tirugatigantla","tolakari","udupula","uppara","vaikhanasa-brahmans","vipravinodulu","yata","yatagiri","yatla","yite"]}, +{"id":"india-ar","iso":"IN-AR","kind":"state","name":"Arunachal Pradesh","shapeName":"Arunāchal Pradesh","bbox":[91.546,26.651,97.411,29.462],"count":7,"slugs":["khamba","khamyang","khowa","momba","na","serdukpen","zakhrin"]}, +{"id":"india-as","iso":"IN-AS","kind":"state","name":"Assam","shapeName":"Assam","bbox":[89.699,24.136,96.018,27.972],"count":15,"slugs":["aiton","bilaspuri","chamar-assamese","hajjam-assamese","kumhar-assamese","man","maria-muslims","matak","mukhi","nai-assamese","namasudra-assamese","sudra-das","taiphake","teli-assamese","turun"]}, +{"id":"india-br","iso":"IN-BR","kind":"state","name":"Bihar","shapeName":"Bihār","bbox":[83.32,24.286,88.3,27.522],"count":18,"slugs":["banpar","camma","dhamin","gadihar","gandharv","hindu-santarash","jains-maithili","jogi-angika","kadar","mukeri","nalband","rajdhop","sahisia","shershabadia","thakurai","tikulhara","tilli","turuk-pasi"]}, +{"id":"india-ch","iso":"IN-CH","kind":"union_territory","name":"Chandigarh","shapeName":"Chandīgarh","bbox":[76.705,30.666,76.849,30.795],"count":1,"slugs":["khatik-punjabi"]}, +{"id":"india-ct","iso":"IN-CT","kind":"state","name":"Chhattisgarh","shapeName":"Chhattīsgarh","bbox":[80.244,17.782,84.396,24.106],"count":24,"slugs":["badhai-chhattisgarhi","bairagi-chhattisgarhi","bargah","beldar-chhattisgarhi","bhangi-chhattisgarhi","bharewa","bharia-bhumia-chhattisgarhi","bhattra-bhatri","bhunjia","chaikwa","dhakad-halbi","dom-chhattisgarhi","ghadwa","gosain-chhattisgarhi","jogi-halbi","kalal-sadri","khairwar-chhattisgarhi","khatik-chhattisgarhi","kol-chhattisgarhi","munnewar","panara","pando","sayyid-chhattisgarhi","son-koli"]}, +{"id":"india-dh","iso":"IN-DH","kind":"union_territory","name":"Dadra and Nagar Haveli and Daman and Diu","shapeName":"Dādra and Nagar Haveli and Damān and Diu","bbox":[70.876,20.047,73.218,20.743],"count":1,"slugs":["sagharias"]}, +{"id":"india-dl","iso":"IN-DL","kind":"union_territory","name":"Delhi","shapeName":"Delhi","bbox":[76.839,28.405,77.347,28.884],"count":1,"slugs":["khatbune"]}, +{"id":"india-ga","iso":"IN-GA","kind":"state","name":"Goa","shapeName":"Goa","bbox":[73.675,14.901,74.336,15.801],"count":5,"slugs":["bania-konkani","gabit","gomantak-maratha","mhalo","mith-gauda"]}, +{"id":"india-gj","iso":"IN-GJ","kind":"state","name":"Gujarat","shapeName":"Gujarāt","bbox":[68.093,20.12,74.477,24.712],"count":90,"slugs":["agaria","agri","anjana-patel-gujarati","bafan","bairagi-gujarati","baloch","bamcha","banjania","barot","barthari","bhodala","borpi","caki","champta","cunara","dafer","dangasiya","dasnami-bawa","depala","dudhwala","gadhai","galiara","garmatan","garvi","gavli","ghantiya","golarana","gosain-gujarati","hajjam-gujarati","halaypotra","hingora","hingorza","jat-muslim","juneja","kahar-gujarati","kamalia","kambodia-bhagat","kapadi","kasar-gujarati","kasbati","kathi","ker","khalpa","khaskeli","khatik-gujarati","langa","machhiyara","majothi","makrani","mandali-muslims","mer","mir","miyana","moghol-gujarati","mujavar","muslim-khatri","muslim-lakhera","muslim-patel","mutwa","narsinghpura","nayak-muslims","node","padhar","panar","paradhi","patanwadia","patni-jamat","patni-vankars","pomla","rathodia","raysipotra","royma","sagar","sandhai-muslims","saranias","sayyid-gujarati","shravan","siddi","sompura-salat","tai","targala","theba","tirgar","turi-barot","vadi","vahivanca-barot","vayati","vyapari","wagheri","wandharo"]}, +{"id":"india-hp","iso":"IN-HP","kind":"state","name":"Himachal Pradesh","shapeName":"Himāchal Pradesh","bbox":[75.594,30.378,79.009,33.257],"count":20,"slugs":["bhojki","daule","dhaki","dosali","gosain-kangri","hadi","hesi","khatik-chambeali","malaneese","masku","nar","nicar","pangwal-buddhists","pangwala","rehar","sanhai","sansoi","seok","swangla","thavi"]}, +{"id":"india-hr","iso":"IN-HR","kind":"state","name":"Haryana","shapeName":"Haryāna","bbox":[74.474,27.653,77.604,30.929],"count":4,"slugs":["arain-punjabi","hajjam-haryanvi","kucband","lakhera-haryanvi"]}, +{"id":"india-jh","iso":"IN-JH","kind":"state","name":"Jharkhand","shapeName":"Jhārkhand","bbox":[83.33,21.97,87.962,25.349],"count":9,"slugs":["dhekaro","jadupetia","jarkhandi-hajjam","jarkhandi-julaha","jharkhandi-nai","modaks","nagbansi","puran","sukiyar"]}, +{"id":"india-jk","iso":"IN-JK","kind":"union_territory","name":"Jammu and Kashmir","shapeName":"Jammu and Kashmīr","bbox":[73.39,32.276,76.78,35.118],"count":33,"slugs":["arain-dogri","bania-dogri","bhand-kashmiri","bomba","budhan","cheintz","chopan","dhobi-dogri","dhyar","domal","galwan","ganz","gardi","gorkun","hajjam-kashmiri","kawoj","kraal","lohar-dogri","manhas","manjygur-muslims","moghol-dogri","nagbansi-dogri","najjar","namdagur","naqqas","pohul","ratal","sayyid-pahari","shaksaz","shina-india","sofi","sudhan","waza"]}, +{"id":"india-ka","iso":"IN-KA","kind":"state","name":"Karnataka","shapeName":"Karnātaka","bbox":[74.086,11.595,78.588,18.459],"count":81,"slugs":["adiyan","agasa","ansari-momin-kannada","arasu","ayri","bagga","baira","balasantoshi","bandi","bania-kannada","batada","bayil-kammara","bayil-pattar","bellara","bestha","chaptegar","charodi","chenna-dasar","dombi-dasa","durga-murga","galada-kokani","ganti-chores","gatti","gauli","ghadi","godda","gond-kannada","gudikara","guraw","hajjam-kannada","halleer","harkantra","hasalaru","helawa","holeya-dasari","jatigar","jaweri","jogi-telugu","jogi-tulu","khakar","kolari","komara-panta","koosa","koraga-korra","kottari","koyava","kudiya","kudubi","kummara-kumbara","kurahina-setti","ladar","maila","malaikudi","maleru","manser","meda","mogaveera","mundala","nadors","nai-kannada","nairy","natua","nawaiyat","padarthis","padiyar","pambada","pattegar","pendar","pinjara-kannada","rachavar","raddi","rajapuri","raneyar","raval","raya-rawat","sapalyas","sayyid-kannada","soligaru","sudir","thammadi","vilyagaras"]}, +{"id":"india-kl","iso":"IN-KL","kind":"state","name":"Kerala","shapeName":"Kerala","bbox":[74.868,8.292,77.412,12.796],"count":72,"slugs":["allar","ambalavasi","anthur-nair","arayavati","ayyanavar","bakuda","bania-malayalam","bowis-mogeyar","cakkala","cakkyar-nambiyar","chakkiyar","chamar-muchi-malayalam","chengazhi-nambiar","cingatanmar","dheevara","erawallan","ezhavathi","ilayatu","kakkalan","kalanadies","kalladi","kallattu-kurup","karimpalan","kavara","kavuthiyan","kidaran","kocu-wellan","kollan","kootan","kumar-kshatriya","kummara-kusuvan","kunduvadian","kurukkal","malai-pandaram","malayalar","maran-nairs","marasari","mavilan","moger-malayalam","moothan","mukkari","musari","muttatu","nalkadaya","nambutiris","narsanna","nasuvan","nayadi","otans","padannan","palakad-tandans","pandithar","pathinaickans","pathiyan","pisaroti","poduval","pulisari","puspakan-unni","reddi","samanthan","thachanad-moopan","tiyattunni","urindavan","vadugan","valinjiyar","vetan","vilkurup","visavan","wainadan-cetti","wainadan-kader","wainadan-pulayan","wayanad-pathiyan"]}, +{"id":"india-la","iso":"IN-LA","kind":"union_territory","name":"Ladakh","shapeName":"Ladākh","bbox":[72.529,32.338,80.327,37.077],"count":6,"slugs":["bania-kashmiri","brukpa","campa","dokhpa","mon-india","purig-pa"]}, +{"id":"india-ld","iso":"IN-LD","kind":"union_territory","name":"Lakshadweep","shapeName":"Lakshadweep","bbox":[72.165,8.264,73.702,11.704],"count":1,"slugs":["laccadive"]}, +{"id":"india-mh","iso":"IN-MH","kind":"state","name":"Maharashtra","shapeName":"Mahārāshtra","bbox":[72.655,15.606,80.898,22.034],"count":54,"slugs":["attar-saibulu","baharupi","barda","bari-marathi","bharwad","bhoi-marathi","chari","chitari","citrakathi","dhawad","dom-marathi","garodi","garpagari","gondhali","gopal","hardas","hatkar","jhadi","kachchi-kushwaha","kaghzi","kapewar","kasikapadi","kathar","kharvi","khatik-marathi","kokaniz","kolhati","kudimahan","ladsi","lajjhar","lodha-marathi","lonar","mana","mitna","mondiwar","muslim-kachera","nahal","naikpod","nandiwala","neeli","nilkanth","pangul","phularis","pinjara-marathi","pochammalollu","prabhu-patane","sahi","sanghar","sarodi","sutar-mistry","takari","velip","waidu","yelmar"]}, +{"id":"india-ml","iso":"IN-ML","kind":"state","name":"Meghalaya","shapeName":"Meghālaya","bbox":[89.814,25.029,92.803,26.12],"count":2,"slugs":["banai","dalu-india"]}, +{"id":"india-mn","iso":"IN-MN","kind":"state","name":"Manipur","shapeName":"Manipur","bbox":[92.971,23.833,94.745,25.692],"count":5,"slugs":["koiren","lois","pangans","thanga","yatibi"]}, +{"id":"india-mp","iso":"IN-MP","kind":"state","name":"Madhya Pradesh","shapeName":"Madhya Pradesh","bbox":[74.03,21.07,82.81,26.869],"count":33,"slugs":["anjana","banchada","baraua","bari-hindi","basudeva","berar","bhanumati","bhurtia","dhed","gachha","hajjam-hindi","jadam","jains-hindi","kachera-bagheli","kandera","kasar-hindi","ladia","lahangir","manbhav","mankar","muskhan","muslim-gond","naita","namdeo","naoda","nat-bagheli","nema","rujjhar","sais","sikligar","sisgars","soria","zamral"]}, +{"id":"india-mz","iso":"IN-MZ","kind":"state","name":"Mizoram","shapeName":"Mizoram","bbox":[92.257,21.941,93.436,24.521],"count":0,"slugs":[]}, +{"id":"india-nl","iso":"IN-NL","kind":"state","name":"Nagaland","shapeName":"Nāgāland","bbox":[93.327,25.199,95.242,27.036],"count":0,"slugs":[]}, +{"id":"india-or","iso":"IN-OR","kind":"state","name":"Odisha","shapeName":"Odisha","bbox":[81.388,17.813,87.486,22.568],"count":64,"slugs":["baha-i","bairagi-odia","bangti","banka","bari-odia","bauti","bavuri","bhoi-odia","bondo","brahman-jyoti","candhai-maru","chatarkheya","cherua","citrakar","daitapati","damla","darji-odia","dhera","didayi","eneti-dora","ghantarghada","ghogia","ghusuria","godra","guna","irika","janughanta","jogi-odia","kachchi-alia","kamila","kapasiya","kasar-odia","khadura","khandwal","khati-odia","kobari","koraga","kuda","kuli","kurunga","kusta","liari","maghia","mahuria","malia","markande-kanjia","mundapotta","nai-odia","naksia","noliya","oriya","panka-odia","parenga","radhi","rana","sabakhia","samasi","sanei","sayyid-odia","sinduria","suda","thanapati","tharua","tula-vina"]}, +{"id":"india-pb","iso":"IN-PB","kind":"state","name":"Punjab","shapeName":"Punjab","bbox":[73.88,29.542,76.939,32.513],"count":4,"slugs":["daoli","gagra","pinjara-punjabi","racband"]}, +{"id":"india-py","iso":"IN-PY","kind":"union_territory","name":"Puducherry","shapeName":"Puducherry","bbox":[75.527,10.827,82.344,16.761],"count":5,"slugs":["indo-french","indo-vietnamese","sonavar","vettalaikarans","vettiyan"]}, +{"id":"india-rj","iso":"IN-RJ","kind":"state","name":"Rajasthan","shapeName":"Rājasthān","bbox":[69.484,23.061,78.272,30.198],"count":48,"slugs":["badi-marwari","baret","bargi","bhopa","bhutta","cejara","chadwa","chheepi","chobdar","chungar","cudigar","dadupanthi","damami","dhola","ghihara","gosain-rajasthani","gurru","hiranbaj","khadim","kharol","kriyagar","laskari","mangnyar-muslims","moghol-marwari","mogia","murhid","nagda","nai-mewari","nyaria","pajabgar","pakhimara","rajkumhar","rajpurohit","rath","runwalas","sain","sanchia","santia","saraogi","shikari","singiwala","sorgars","sunga","tak","usta","van-baoriya","ved","vijabargiya"]}, +{"id":"india-sk","iso":"IN-SK","kind":"state","name":"Sikkim","shapeName":"Sikkim","bbox":[88.014,27.079,88.92,28.129],"count":1,"slugs":["yakha"]}, +{"id":"india-tg","iso":"IN-TG","kind":"state","name":"Telangana","shapeName":"Telangāna","bbox":[77.236,15.836,81.323,19.916],"count":26,"slugs":["are-kapu","attagara","bhatrajulu","bindla","bolla","cippollu","gangireddula","gosangi","gurika","jains-telugu","kachi","katipamula","khatik-telugu","lakkamari-kapu","mala-masti","matangi","mitha-ayyalavar","moghal-telugu","muchi-chamar","pambala","pariki-muggula","patkars","rajannalu","saradagallu","sindhollu","verra-musti"]}, +{"id":"india-tn","iso":"IN-TN","kind":"state","name":"Tamil Nadu","shapeName":"Tamil Nādu","bbox":[76.233,8.077,80.347,13.565],"count":64,"slugs":["alavan","ansari-momin-tamil","anuppan","aranadan","baig","bondili","cekkala","chamar-muchi-tamil","chayakkaran","dasa-palanjika","dayare","dhobi-tamil","gangawar","gudugudupandi","gurukkal","jains-tamil","jati-pillai","kambalattan","kambar","kannada-naicker","kannada-saineegar","karaiyar","karlar","karna-sale","katesar","kattiakaran","kayalar","kepmari","koliyan","kongu-vaisnawa","kongu-vellala-gounder","kota","krishnavaka","kudumi","kuluvan","kummara-kummari","kunnuva-vellalar","malai-vellala","maleyan","maniyattikkaran","mudugar","nakkala","namdeo-maratha","nanjil-mudali","pannadi","panniandi","parkavakulam","parvatarajakulam","pattanavan","perumkollar","salivahanam","savalakkarar","sayyid-tamil","semman","senaithalaivar","siviar","tambiran","tholuva-naicker","thoraiyar","tondman","uppillian","urikkar-naicker","vellaiyan-kuppam-padaiyaci","vettaikkaran"]}, +{"id":"india-tr","iso":"IN-TR","kind":"state","name":"Tripura","shapeName":"Tripura","bbox":[91.15,22.939,92.332,24.528],"count":11,"slugs":["bajandar","banik-bania","barua","haluadas","kanda","mahisyadas","malakar","sabar","saha","shabdakar","tripuri-namasudra"]}, +{"id":"india-up","iso":"IN-UP","kind":"state","name":"Uttar Pradesh","shapeName":"Uttar Pradesh","bbox":[77.084,23.87,84.635,30.406],"count":23,"slugs":["ahmadiya","arain-kannauji","badhik","badi","balahar","bargala","bargujar","bhuiyar","bisati","charwaha","gadhia","habura","hijra","kankali","khangar-bundeli","mujavir","pankhiya","patari","putliwale","rai-bhat-muslims","rastogi","rave","sheikhzade"]}, +{"id":"india-ut","iso":"IN-UT","kind":"state","name":"Uttarakhand","shapeName":"Uttarākhand","bbox":[77.573,28.723,81.045,31.456],"count":7,"slugs":["anwal","chaudangsi","jimdar","kuthliya-bora","raji-india","sayyid-garhwali","tagewale"]}, +{"id":"india-wb","iso":"IN-WB","kind":"state","name":"West Bengal","shapeName":"West Bengal","bbox":[85.82,21.484,89.874,27.221],"count":30,"slugs":["angarok","ansari-momin-bengali","bagti","baida","baisya","bhakta","bhalla","bhujel","byadh","chunari","dhal-kstriya","dhimal","ghazi","hajjam-bengali","jains-bengali","kakmara","kapuria","kasar-bengali","khairwar-bengali","khalifa","khatik-bengali","kurol","mirshikar","paharia","pinjara-dhunia-bengali","siyalgar","sunuwar","sutradhar","tamboli-bengali","tripuri-kumhar"]} +]}