spa-l2-cairo-shop-knn

Status: done
Score: 1.000
Duration: 3:07
Cost: 2.57¢
Model: deepseek/deepseek-v4-flash

Map (reference ← swipe → agent)

0:00
I'm putting together a retail-density readout for a downtown Cairo brief. `cairo_retail` has two layers, `shops` and `anchors`. For each anchor I need the 5 nearest shops with distance in metres and a within-1 km flag, plus a 5x3 distance matrix from those 5 shops to the anchor's 3 closest sibling anchors. The chain names in the source data have inconsistent spellings, so the output should use consistent names: the same `shop_id` always carries the same `normalised_name`, and all shops belonging to one chain share a single canonical spelling. Tidy the anchor names too. Please write `market_neighbourhoods.json` as a top-level JSON array with one record per anchor. Each record carries the keys `anchor_id`, `anchor_name_normalised` (non-empty string), `knn` (exactly 5 entries sorted by ascending `distance_m`), and `full_distance_matrix_m` (5 rows by 3 numeric columns; rows follow the knn order, columns correspond to the sibling anchors in distance-ascending order). Each `knn` entry has `shop_id`, `normalised_name`, `distance_m` (finite numeric, in metres), and `within_1km` (boolean, true when `distance_m` is at most 1000).
0:00
System info
{
  "adapter": "openrouter",
  "model": "deepseek/deepseek-v4-flash",
  "base_url": "https://openrouter.ai/api/v1"
}
0:11
0:11
Bash ls /work/
cairo_retail.gpkg
0:22
0:22
Bash cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
gpd.list_layers('cairo_retail.gpkg')
"
(no output)
0:32
0:32
Bash cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
shops = gpd.read_file('cairo_retail.gpkg', layer='shops')
anchors = gpd.read_file('cairo_retail.gpkg', layer='anchors')
print('=== SHOPS ===')
print(shops.columns.tolist())
print(shops.head(10).to_string())
print()
print(shops.dtypes)
print()
print('=== ANCHORS ===')
print(anchors.columns.tolist())
print(anchors.head(10).to_string())
print()
print(anchors.dtypes)
"
=== SHOPS ===
['shop_id', 'raw_name', 'geometry']
  shop_id          raw_name                       geometry
0  S00001             OSCAR  POINT (637478.058 814957.398)
1  S00002  Local Shop 00002  POINT (651773.775 816149.979)
2  S00003  Local Shop 00003  POINT (640341.689 815148.396)
3  S00004  Local Shop 00004  POINT (640225.465 805311.132)
4  S00005  Local Shop 00005  POINT (647199.272 818195.959)
5  S00006              مترو  POINT (647363.601 817078.397)
6  S00007  Local Shop 00007  POINT (637452.604 813452.721)
7  S00008         Carrefour  POINT (642884.749 820068.564)
8  S00009  Local Shop 00009  POINT (638401.854 815382.513)
9  S00010         Carrefour   POINT (645066.08 808334.791)

shop_id          str
raw_name         str
geometry    geometry
dtype: object

=== ANCHORS ===
['anchor_id', 'anchor_name', 'geometry']
  anchor_id          anchor_name                       geometry
0      M001  TAHRIR SQUARE PLAZA  POINT (635109.343 805565.909)
1      M002    Zamalek Riverside  POINT (637039.843 805569.597)
2      M003     Maadi Corniche     POINT (638970.345 805573.62)
3      M004    HELIOPOLIS SQUARE  POINT (640900.848 805577.981)
4      M005    New Cairo Tagamoa  POINT (642831.353 805582.677)
5      M006      Nasr City Hub     POINT (644761.86 805587.711)
6      M007   MOHANDESSIN CENTRE  POINT (646692.368 805593.081)
7      M008     Garden City Walk  POINT (648622.879 805598.787)
8      M009       Dokki Market     POINT (650553.391 805604.83)
9      M010   GIZA PYRAMIDS GATE   POINT (652483.906 805611.21)

anchor_id           str
anchor_name         str
geometry       geometry
dtype: object
0:40
0:40
Bash cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
shops = gpd.read_file('cairo_retail.gpkg', layer='shops')
anchors = gpd.read_file('cairo_retail.gpkg', layer='anchors')
print('=== Unique shop raw_names ===')
for n in sorted(shops['raw_name'].unique()):
    print(repr(n))
print()
print('=== Unique anchor names ===')
for n in sorted(anchors['anchor_name'].unique()):
    print(repr(n))
print()
print(f'Total shops: {len(shops)}')
print(f'Total anchors: {len(anchors)}')
"
=== Unique shop raw_names ===
'Abu Zekry'
'Abu Zikri'
'Carrefour'
'Carrefour Egypt'
'Hyper One'
'HyperOne'
'Khair Zaman'
'Kheir Zaman'
'Local Shop 00002'
'Local Shop 00003'
'Local Shop 00004'
'Local Shop 00005'
'Local Shop 00007'
'Local Shop 00009'
'Local Shop 00011'
'Local Shop 00013'
'Local Shop 00014'
'Local Shop 00015'
'Local Shop 00017'
'Local Shop 00018'
'Local Shop 00019'
'Local Shop 00020'
'Local Shop 00023'
'Local Shop 00024'
'Local Shop 00025'
'Local Shop 00027'
'Local Shop 00030'
'Local Shop 00031'
'Local Shop 00033'
'Local Shop 00034'
'Local Shop 00035'
'Local Shop 00036'
'Local Shop 00037'
'Local Shop 00038'
'Local Shop 00040'
'Local Shop 00041'
'Local Shop 00048'
'Local Shop 00049'
'Local Shop 00050'
'Local Shop 00052'
'Local Shop 00053'
'Local Shop 00055'
'Local Shop 00056'
'Local Shop 00059'
'Local Shop 00063'
'Local Shop 00067'
'Local Shop 00069'
'Local Shop 00071'
'Local Shop 00072'
'Local Shop 00075'
'Local Shop 00076'
'Local Shop 00077'
'Local Shop 00079'
'Local Shop 00080'
'Local Shop 00081'
'Local Shop 00082'
'Local Shop 00084'
'Local Shop 00085'
'Local Shop 00086'
'Local Shop 00088'
'Local Shop 00090'
'Local Shop 00091'
'Local Shop 00092'
'Local Shop 00093'
'Local Shop 00095'
'Local Shop 00096'
'Local Shop 00103'
'Local Shop 00104'
'Local Shop 00105'
'Local Shop 00106'
'Local Shop 00110'
'Local Shop 00111'
'Local Shop 00113'
'Local Shop 00119'
'Local Shop 00120'
'Local Shop 00121'
'Local Shop 00125'
'Local Shop 00127'
'Local Shop 00135'
'Local Shop 00136'
'Local Shop 00137'
'Local Shop 00138'
'Local Shop 00139'
'Local Shop 00142'
'Local Shop 00143'
'Local Shop 00144'
'Local Shop 00146'
'Local Shop 00150'
'Local Shop 00151'
'Local Shop 00152'
'Local Shop 00153'
'Local Shop 00157'
'Local Shop 00158'
'Local Shop 00159'
'Local Shop 00161'
'Local Shop 00162'
'Local Shop 00164'
'Local Shop 00165'
'Local Shop 00166'
'Local Shop 00167'
'Local Shop 00169'
'Local Shop 00170'
'Local Shop 00171'
'Local Shop 00172'
'Local Shop 00176'
'Local Shop 00181'
'Local Shop 00182'
'Local Shop 00184'
'Local Shop 00185'
'Local Shop 00187'
'Local Shop 00188'
'Local Shop 00190'
'Local Shop 00191'
'Local Shop 00192'
'Local Shop 00194'
'Local Shop 00195'
'Local Shop 00197'
'Local Shop 00198'
'Local Shop 00200'
'Local Shop 00201'
'Local Shop 00202'
'Local Shop 00203'
'Local Shop 00205'
'Local Shop 00206'
'Local Shop 00209'
'Local Shop 00210'
'Local Shop 00211'
'Local Shop 00213'
'Local Shop 00214'
'Local Shop 00216'
'Local Shop 00217'
'Local Shop 00222'
'Local Shop 00225'
'Local Shop 00227'
'Local Shop 00228'
'Local Shop 00229'
'Local Shop 00230'
'Local Shop 00231'
'Local Shop 00233'
'Local Shop 00239'
'Local Shop 00241'
'Local Shop 00242'
'Local Shop 00244'
'Local Shop 00245'
'Local Shop 00246'
'Local Shop 00250'
'Local Shop 00251'
'Local Shop 00258'
'Local Shop 00259'
'Local Shop 00260'
'Local Shop 00262'
'Local Shop 00264'
'Local Shop 00266'
'Local Shop 00268'
'Local Shop 00269'
'Local Shop 00271'
'Local Shop 00275'
'Local Shop 00276'
'Local Shop 00278'
'Local Shop 00279'
'Local Shop 00280'
'Local Shop 00282'
'Local Shop 00283'
'Local Shop 00286'
'Local Shop 00289'
'Local Shop 00295'
'Local Shop 00298'
'Local Shop 00300'
'Local Shop 00301'
'Local Shop 00303'
'Local Shop 00305'
'Local Shop 00306'
'Local Shop 00307'
'Local Shop 00309'
'Local Shop 00311'
'Local Shop 00312'
'Local Shop 00313'
'Local Shop 00314'
'Local Shop 00315'
'Local Shop 00319'
'Local Shop 00320'
'Local Shop 00322'
'Local Shop 00323'
'Local Shop 00325'
'Local Shop 00328'
'Local Shop 00334'
'Local Shop 00335'
'Local Shop 00337'
'Local Shop 00338'
'Local Shop 00342'
'Local Shop 00346'
'Local Shop 00347'
'Local Shop 00350'
'Local Shop 00351'
'Local Shop 00353'
'Local Shop 00354'
'Local Shop 00357'
'Local Shop 00361'
'Local Shop 00362'
'Local Shop 00363'
'Local Shop 00365'
'Local Shop 00366'
'Local Shop 00367'
'Local Shop 00372'
'Local Shop 00375'
'Local Shop 00376'
'Local Shop 00378'
'Local Shop 00379'
'Local Shop 00380'
'Local Shop 00385'
'Local Shop 00387'
'Local Shop 00392'
'Local Shop 00394'
'Local Shop 00395'
'Local Shop 00397'
'Local Shop 00400'
'Local Shop 00403'
'Local Shop 00408'
'Local Shop 00409'
'Local Shop 00410'
'Local Shop 00413'
'Local Shop 00417'
'Local Shop 00418'
'Local Shop 00420'
'Local Shop 00423'
'Local Shop 00424'
'Local Shop 00427'
'Local Shop 00429'
'Local Shop 00432'
'Local Shop 00433'
'Local Shop 00434'
'Local Shop 00435'
'Local Shop 00437'
'Local Shop 00439'
'Local Shop 00441'
'Local Shop 00442'
'Local Shop 00443'
'Local Shop 00447'
'Local Shop 00448'
'Local Shop 00450'
'Local Shop 00452'
'Local Shop 00455'
'Local Shop 00456'
'Local Shop 00457'
'Local Shop 00459'
'Local Shop 00460'
'Local Shop 00462'
'Local Shop 00463'
'Local Shop 00465'
'Local Shop 00466'
'Local Shop 00468'
'Local Shop 00470'
'Local Shop 00471'
'Local Shop 00472'
'Local Shop 00473'
'Local Shop 00474'
'Local Shop 00477'
'Local Shop 00478'
'Local Shop 00479'
'Local Shop 00480'
'Local Shop 00481'
'Local Shop 00483'
'Local Shop 00484'
'Local Shop 00486'
'Local Shop 00487'
'Local Shop 00488'
'Local Shop 00489'
'Local Shop 00491'
'Local Shop 00493'
'Local Shop 00494'
'Local Shop 00496'
'Local Shop 00498'
'Local Shop 00499'
'Local Shop 00502'
'Local Shop 00503'
'Local Shop 00505'
'Local Shop 00506'
'Local Shop 00507'
'Local Shop 00509'
'Local Shop 00511'
'Local Shop 00513'
'Local Shop 00514'
'Local Shop 00515'
'Local Shop 00516'
'Local Shop 00517'
'Local Shop 00518'
'Local Shop 00520'
'Local Shop 00521'
'Local Shop 00524'
'Local Shop 00525'
'Local Shop 00527'
'Local Shop 00528'
'Local Shop 00529'
'Local Shop 00530'
'Local Shop 00531'
'Local Shop 00532'
'Local Shop 00536'
'Local Shop 00538'
'Local Shop 00541'
'Local Shop 00545'
'Local Shop 00547'
'Local Shop 00548'
'Local Shop 00549'
'Local Shop 00550'
'Local Shop 00551'
'Local Shop 00552'
'Local Shop 00556'
'Local Shop 00557'
'Local Shop 00560'
'Local Shop 00561'
'Local Shop 00562'
'Local Shop 00563'
'Local Shop 00566'
'Local Shop 00567'
'Local Shop 00568'
'Local Shop 00570'
'Local Shop 00571'
'Local Shop 00573'
'Local Shop 00574'
'Local Shop 00577'
'Local Shop 00578'
'Local Shop 00579'
'Local Shop 00581'
'Local Shop 00583'
'Local Shop 00585'
'Local Shop 00586'
'Local Shop 00588'
'Local Shop 00589'
'Local Shop 00590'
'Local Shop 00591'
'Local Shop 00593'
'Local Shop 00594'
'Local Shop 00596'
'Local Shop 00597'
'Local Shop 00601'
'Local Shop 00602'
'Local Shop 00603'
'Local Shop 00604'
'Local Shop 00608'
'Local Shop 00610'
'Local Shop 00611'
'Local Shop 00612'
'Local Shop 00613'
'Local Shop 00615'
'Local Shop 00616'
'Local Shop 00617'
'Local Shop 00618'
'Local Shop 00619'
'Local Shop 00623'
'Local Shop 00624'
'Local Shop 00637'
'Local Shop 00640'
'Local Shop 00641'
'Local Shop 00642'
'Local Shop 00643'
'Local Shop 00648'
'Local Shop 00650'
'Local Shop 00653'
'Local Shop 00658'
'Local Shop 00659'
'Local Shop 00660'
'Local Shop 00661'
'Local Shop 00662'
'Local Shop 00663'
'Local Shop 00664'
'Local Shop 00665'
'Local Shop 00666'
'Local Shop 00667'
'Local Shop 00669'
'Local Shop 00673'
'Local Shop 00674'
'Local Shop 00675'
'Local Shop 00676'
'Local Shop 00678'
'Local Shop 00680'
'Local Shop 00681'
'Local Shop 00683'
'Local Shop 00687'
'Local Shop 00688'
'Local Shop 00690'
'Local Shop 00693'
'Local Shop 00695'
'Local Shop 00697'
'Local Shop 00699'
'Local Shop 00707'
'Local Shop 00708'
'Local Shop 00710'
'Local Shop 00711'
'Local Shop 00712'
'Local Shop 00714'
'Local Shop 00716'
'Local Shop 00718'
'Local Shop 00719'
'Local Shop 00720'
'Local Shop 00721'
'Local Shop 00722'
'Local Shop 00731'
'Local Shop 00734'
'Local Shop 00735'
'Local Shop 00736'
'Local Shop 00737'
'Local Shop 00739'
'Local Shop 00745'
'Local Shop 00746'
'Local Shop 00749'
'Local Shop 00751'
'Local Shop 00755'
'Local Shop 00757'
'Local Shop 00762'
'Local Shop 00765'
'Local Shop 00773'
'Local Shop 00774'
'Local Shop 00776'
'Local Shop 00778'
'Local Shop 00779'
'Local Shop 00782'
'Local Shop 00786'
'Local Shop 00787'
'Local Shop 00789'
'Local Shop 00790'
'Local Shop 00791'
'Local Shop 00792'
'Local Shop 00794'
'Local Shop 00795'
'Local Shop 00797'
'Local Shop 00800'
'Local Shop 00801'
'Local Shop 00804'
'Local Shop 00808'
'Local Shop 00810'
'Local Shop 00812'
'Local Shop 00813'
'Local Shop 00814'
'Local Shop 00815'
'Local Shop 00816'
'Local Shop 00819'
'Local Shop 00821'
'Local Shop 00824'
'Local Shop 00826'
'Local Shop 00827'
'Local Shop 00829'
'Local Shop 00835'
'Local Shop 00836'
'Local Shop 00838'
'Local Shop 00841'
'Local Shop 00849'
'Local Shop 00850'
'Local Shop 00851'
'Local Shop 00853'
'Local Shop 00855'
'Local Shop 00857'
'Local Shop 00859'
'Local Shop 00863'
'Local Shop 00864'
'Local Shop 00866'
'Local Shop 00870'
'Local Shop 00871'
'Local Shop 00875'
'Local Shop 00876'
'Local Shop 00879'
'Local Shop 00880'
'Local Shop 00881'
'Local Shop 00882'
'Local Shop 00888'
'Local Shop 00890'
'Local Shop 00891'
'Local Shop 00893'
'Local Shop 00898'
'Local Shop 00900'
'Local Shop 00901'
'Local Shop 00902'
'Local Shop 00903'
'Local Shop 00910'
'Local Shop 00913'
'Local Shop 00914'
'Local Shop 00917'
'Local Shop 00918'
'Local Shop 00919'
'Local Shop 00921'
'Local Shop 00922'
'Local Shop 00932'
'Local Shop 00938'
'Local Shop 00940'
'Local Shop 00943'
'Local Shop 00945'
'Local Shop 00949'
'Local Shop 00950'
'Local Shop 00951'
'Local Shop 00952'
'Local Shop 00953'
'Local Shop 00954'
'Local Shop 00955'
'Local Shop 00958'
'Local Shop 00961'
'Local Shop 00962'
'Local Shop 00966'
'Local Shop 00969'
'Local Shop 00971'
'Local Shop 00973'
'Local Shop 00976'
'Local Shop 00977'
'Local Shop 00978'
'Local Shop 00979'
'Local Shop 00980'
'Local Shop 00981'
'Local Shop 00983'
'Local Shop 00984'
'Local Shop 00992'
'Local Shop 00999'
'Local Shop 01000'
'Local Shop 01006'
'Local Shop 01007'
'Local Shop 01008'
'Local Shop 01011'
'Local Shop 01012'
'Local Shop 01015'
'Local Shop 01018'
'Local Shop 01025'
'Local Shop 01026'
'Local Shop 01027'
'Local Shop 01030'
'Local Shop 01031'
'Local Shop 01032'
'Local Shop 01033'
'Local Shop 01034'
'Local Shop 01036'
'Local Shop 01038'
'Local Shop 01039'
'Local Shop 01041'
'Local Shop 01042'
'Local Shop 01044'
'Local Shop 01047'
'Local Shop 01049'
'Local Shop 01051'
'Local Shop 01053'
'Local Shop 01054'
'Local Shop 01057'
'Local Shop 01058'
'Local Shop 01059'
'Local Shop 01066'
'Local Shop 01069'
'Local Shop 01070'
'Local Shop 01074'
'Local Shop 01076'
'Local Shop 01079'
'Local Shop 01082'
'Local Shop 01083'
'Local Shop 01085'
'Local Shop 01088'
'Local Shop 01090'
'Local Shop 01091'
'Local Shop 01093'
'Local Shop 01095'
'Local Shop 01096'
'Local Shop 01099'
'Local Shop 01102'
'Local Shop 01103'
'Local Shop 01105'
'Local Shop 01106'
'Local Shop 01107'
'Local Shop 01108'
'Local Shop 01109'
'Local Shop 01111'
'Local Shop 01113'
'Local Shop 01115'
'Local Shop 01119'
'Local Shop 01123'
'Local Shop 01124'
'Local Shop 01126'
'Local Shop 01127'
'Local Shop 01128'
'Local Shop 01130'
'Local Shop 01131'
'Local Shop 01132'
'Local Shop 01134'
'Local Shop 01136'
'Local Shop 01137'
'Local Shop 01138'
'Local Shop 01139'
'Local Shop 01140'
'Local Shop 01142'
'Local Shop 01145'
'Local Shop 01147'
'Local Shop 01149'
'Local Shop 01151'
'Local Shop 01154'
'Local Shop 01158'
'Local Shop 01159'
'Local Shop 01160'
'Local Shop 01162'
'Local Shop 01163'
'Local Shop 01166'
'Local Shop 01167'
'Local Shop 01168'
'Local Shop 01169'
'Local Shop 01172'
'Local Shop 01173'
'Local Shop 01175'
'Local Shop 01176'
'Local Shop 01180'
'Local Shop 01181'
'Local Shop 01182'
'Local Shop 01183'
'Local Shop 01186'
'Local Shop 01188'
'Local Shop 01189'
'Local Shop 01190'
'Local Shop 01191'
'Local Shop 01196'
'Local Shop 01201'
'Local Shop 01202'
'Local Shop 01204'
'Local Shop 01206'
'Local Shop 01207'
'Local Shop 01209'
'Local Shop 01211'
'Local Shop 01212'
'Local Shop 01213'
'Local Shop 01215'
'Local Shop 01216'
'Local Shop 01221'
'Local Shop 01222'
'Local Shop 01225'
'Local Shop 01231'
'Local Shop 01232'
'Local Shop 01234'
'Local Shop 01240'
'Local Shop 01241'
'Local Shop 01242'
'Local Shop 01243'
'Local Shop 01244'
'Local Shop 01245'
'Local Shop 01246'
'Local Shop 01249'
'Local Shop 01251'
'Local Shop 01254'
'Local Shop 01255'
'Local Shop 01258'
'Local Shop 01259'
'Local Shop 01261'
'Local Shop 01262'
'Local Shop 01263'
'Local Shop 01266'
'Local Shop 01269'
'Local Shop 01271'
'Local Shop 01272'
'Local Shop 01273'
'Local Shop 01276'
'Local Shop 01277'
'Local Shop 01278'
'Local Shop 01282'
'Local Shop 01283'
'Local Shop 01287'
'Local Shop 01289'
'Local Shop 01290'
'Local Shop 01293'
'Local Shop 01294'
'Local Shop 01297'
'Local Shop 01300'
'Local Shop 01301'
'Local Shop 01302'
'Local Shop 01303'
'Local Shop 01306'
'Local Shop 01314'
'Local Shop 01315'
'Local Shop 01316'
'Local Shop 01318'
'Local Shop 01319'
'Local Shop 01320'
'Local Shop 01325'
'Local Shop 01328'
'Local Shop 01329'
'Local Shop 01330'
'Local Shop 01333'
'Local Shop 01334'
'Local Shop 01335'
'Local Shop 01336'
'Local Shop 01337'
'Local Shop 01341'
'Local Shop 01342'
'Local Shop 01344'
'Local Shop 01346'
'Local Shop 01348'
'Local Shop 01353'
'Local Shop 01355'
'Local Shop 01356'
'Local Shop 01357'
'Local Shop 01358'
'Local Shop 01360'
'Local Shop 01364'
'Local Shop 01367'
'Local Shop 01370'
'Local Shop 01372'
'Local Shop 01375'
'Local Shop 01377'
'Local Shop 01379'
'Local Shop 01380'
'Local Shop 01384'
'Local Shop 01386'
'Local Shop 01387'
'Local Shop 01388'
'Local Shop 01389'
'Local Shop 01391'
'Local Shop 01393'
'Local Shop 01394'
'Local Shop 01395'
'Local Shop 01398'
'Local Shop 01399'
'Local Shop 01403'
'Local Shop 01404'
'Local Shop 01415'
'Local Shop 01416'
'Local Shop 01417'
'Local Shop 01419'
'Local Shop 01421'
'Local Shop 01422'
'Local Shop 01423'
'Local Shop 01426'
'Local Shop 01427'
'Local Shop 01428'
'Local Shop 01429'
'Local Shop 01430'
'Local Shop 01431'
'Local Shop 01433'
'Local Shop 01437'
'Local Shop 01438'
'Local Shop 01441'
'Local Shop 01445'
'Local Shop 01446'
'Local Shop 01450'
'Local Shop 01455'
'Local Shop 01456'
'Local Shop 01457'
'Local Shop 01460'
'Local Shop 01461'
'Local Shop 01462'
'Local Shop 01463'
'Local Shop 01464'
'Local Shop 01466'
'Local Shop 01468'
'Local Shop 01476'
'Local Shop 01481'
'Local Shop 01482'
'Local Shop 01484'
'Local Shop 01485'
'Local Shop 01486'
'Local Shop 01487'
'Local Shop 01488'
'Local Shop 01489'
'Local Shop 01490'
'Local Shop 01493'
'Local Shop 01494'
'Local Shop 01495'
'Local Shop 01496'
'Local Shop 01499'
'Local Shop 01500'
'Local Shop 01501'
'Local Shop 01503'
'Local Shop 01505'
'Local Shop 01506'
'Local Shop 01507'
'Local Shop 01509'
'Local Shop 01510'
'Local Shop 01513'
'Local Shop 01514'
'Local Shop 01518'
'Local Shop 01521'
'Local Shop 01522'
'Local Shop 01523'
'Local Shop 01524'
'Local Shop 01526'
'Local Shop 01527'
'Local Shop 01529'
'Local Shop 01531'
'Local Shop 01532'
'Local Shop 01533'
'Local Shop 01535'
'Local Shop 01545'
'Local Shop 01547'
'Local Shop 01548'
'Local Shop 01552'
'Local Shop 01553'
'Local Shop 01557'
'Local Shop 01559'
'Local Shop 01560'
'Local Shop 01561'
'Local Shop 01562'
'Local Shop 01563'
'Local Shop 01564'
'Local Shop 01565'
'Local 
[... truncated ...]cal Shop 08695'
'Local Shop 08699'
'Local Shop 08700'
'Local Shop 08701'
'Local Shop 08702'
'Local Shop 08705'
'Local Shop 08707'
'Local Shop 08708'
'Local Shop 08709'
'Local Shop 08710'
'Local Shop 08714'
'Local Shop 08715'
'Local Shop 08717'
'Local Shop 08718'
'Local Shop 08719'
'Local Shop 08720'
'Local Shop 08723'
'Local Shop 08724'
'Local Shop 08725'
'Local Shop 08727'
'Local Shop 08728'
'Local Shop 08730'
'Local Shop 08734'
'Local Shop 08736'
'Local Shop 08737'
'Local Shop 08738'
'Local Shop 08739'
'Local Shop 08740'
'Local Shop 08741'
'Local Shop 08742'
'Local Shop 08745'
'Local Shop 08746'
'Local Shop 08748'
'Local Shop 08750'
'Local Shop 08752'
'Local Shop 08755'
'Local Shop 08756'
'Local Shop 08758'
'Local Shop 08759'
'Local Shop 08760'
'Local Shop 08766'
'Local Shop 08768'
'Local Shop 08771'
'Local Shop 08773'
'Local Shop 08774'
'Local Shop 08775'
'Local Shop 08779'
'Local Shop 08780'
'Local Shop 08783'
'Local Shop 08784'
'Local Shop 08785'
'Local Shop 08788'
'Local Shop 08789'
'Local Shop 08790'
'Local Shop 08791'
'Local Shop 08792'
'Local Shop 08793'
'Local Shop 08795'
'Local Shop 08796'
'Local Shop 08798'
'Local Shop 08799'
'Local Shop 08801'
'Local Shop 08802'
'Local Shop 08804'
'Local Shop 08805'
'Local Shop 08807'
'Local Shop 08810'
'Local Shop 08814'
'Local Shop 08815'
'Local Shop 08818'
'Local Shop 08822'
'Local Shop 08827'
'Local Shop 08828'
'Local Shop 08830'
'Local Shop 08834'
'Local Shop 08837'
'Local Shop 08840'
'Local Shop 08841'
'Local Shop 08843'
'Local Shop 08844'
'Local Shop 08846'
'Local Shop 08847'
'Local Shop 08852'
'Local Shop 08853'
'Local Shop 08854'
'Local Shop 08855'
'Local Shop 08856'
'Local Shop 08857'
'Local Shop 08861'
'Local Shop 08863'
'Local Shop 08864'
'Local Shop 08865'
'Local Shop 08868'
'Local Shop 08870'
'Local Shop 08871'
'Local Shop 08872'
'Local Shop 08873'
'Local Shop 08875'
'Local Shop 08879'
'Local Shop 08880'
'Local Shop 08881'
'Local Shop 08883'
'Local Shop 08886'
'Local Shop 08888'
'Local Shop 08890'
'Local Shop 08895'
'Local Shop 08897'
'Local Shop 08898'
'Local Shop 08899'
'Local Shop 08900'
'Local Shop 08901'
'Local Shop 08906'
'Local Shop 08907'
'Local Shop 08910'
'Local Shop 08911'
'Local Shop 08912'
'Local Shop 08916'
'Local Shop 08921'
'Local Shop 08925'
'Local Shop 08927'
'Local Shop 08928'
'Local Shop 08930'
'Local Shop 08931'
'Local Shop 08932'
'Local Shop 08933'
'Local Shop 08934'
'Local Shop 08938'
'Local Shop 08940'
'Local Shop 08941'
'Local Shop 08943'
'Local Shop 08944'
'Local Shop 08945'
'Local Shop 08946'
'Local Shop 08947'
'Local Shop 08949'
'Local Shop 08950'
'Local Shop 08951'
'Local Shop 08955'
'Local Shop 08956'
'Local Shop 08957'
'Local Shop 08958'
'Local Shop 08959'
'Local Shop 08961'
'Local Shop 08962'
'Local Shop 08964'
'Local Shop 08965'
'Local Shop 08966'
'Local Shop 08967'
'Local Shop 08968'
'Local Shop 08969'
'Local Shop 08970'
'Local Shop 08971'
'Local Shop 08974'
'Local Shop 08975'
'Local Shop 08976'
'Local Shop 08982'
'Local Shop 08984'
'Local Shop 08985'
'Local Shop 08989'
'Local Shop 08990'
'Local Shop 08994'
'Local Shop 08996'
'Local Shop 08997'
'Local Shop 08998'
'Local Shop 09001'
'Local Shop 09003'
'Local Shop 09005'
'Local Shop 09007'
'Local Shop 09009'
'Local Shop 09010'
'Local Shop 09012'
'Local Shop 09017'
'Local Shop 09020'
'Local Shop 09021'
'Local Shop 09022'
'Local Shop 09023'
'Local Shop 09024'
'Local Shop 09025'
'Local Shop 09026'
'Local Shop 09028'
'Local Shop 09030'
'Local Shop 09032'
'Local Shop 09034'
'Local Shop 09036'
'Local Shop 09038'
'Local Shop 09040'
'Local Shop 09044'
'Local Shop 09046'
'Local Shop 09047'
'Local Shop 09050'
'Local Shop 09051'
'Local Shop 09052'
'Local Shop 09053'
'Local Shop 09055'
'Local Shop 09056'
'Local Shop 09062'
'Local Shop 09066'
'Local Shop 09067'
'Local Shop 09068'
'Local Shop 09070'
'Local Shop 09072'
'Local Shop 09075'
'Local Shop 09077'
'Local Shop 09078'
'Local Shop 09080'
'Local Shop 09082'
'Local Shop 09084'
'Local Shop 09085'
'Local Shop 09089'
'Local Shop 09090'
'Local Shop 09093'
'Local Shop 09094'
'Local Shop 09095'
'Local Shop 09097'
'Local Shop 09098'
'Local Shop 09099'
'Local Shop 09104'
'Local Shop 09108'
'Local Shop 09110'
'Local Shop 09112'
'Local Shop 09113'
'Local Shop 09115'
'Local Shop 09117'
'Local Shop 09118'
'Local Shop 09123'
'Local Shop 09125'
'Local Shop 09126'
'Local Shop 09128'
'Local Shop 09129'
'Local Shop 09130'
'Local Shop 09131'
'Local Shop 09133'
'Local Shop 09134'
'Local Shop 09138'
'Local Shop 09139'
'Local Shop 09140'
'Local Shop 09141'
'Local Shop 09142'
'Local Shop 09143'
'Local Shop 09145'
'Local Shop 09147'
'Local Shop 09148'
'Local Shop 09149'
'Local Shop 09153'
'Local Shop 09154'
'Local Shop 09156'
'Local Shop 09159'
'Local Shop 09161'
'Local Shop 09164'
'Local Shop 09165'
'Local Shop 09166'
'Local Shop 09172'
'Local Shop 09175'
'Local Shop 09177'
'Local Shop 09180'
'Local Shop 09182'
'Local Shop 09183'
'Local Shop 09184'
'Local Shop 09185'
'Local Shop 09188'
'Local Shop 09190'
'Local Shop 09191'
'Local Shop 09194'
'Local Shop 09195'
'Local Shop 09196'
'Local Shop 09198'
'Local Shop 09199'
'Local Shop 09201'
'Local Shop 09202'
'Local Shop 09206'
'Local Shop 09207'
'Local Shop 09208'
'Local Shop 09209'
'Local Shop 09210'
'Local Shop 09212'
'Local Shop 09213'
'Local Shop 09214'
'Local Shop 09216'
'Local Shop 09217'
'Local Shop 09219'
'Local Shop 09220'
'Local Shop 09221'
'Local Shop 09223'
'Local Shop 09224'
'Local Shop 09225'
'Local Shop 09226'
'Local Shop 09227'
'Local Shop 09232'
'Local Shop 09234'
'Local Shop 09239'
'Local Shop 09241'
'Local Shop 09242'
'Local Shop 09243'
'Local Shop 09246'
'Local Shop 09248'
'Local Shop 09250'
'Local Shop 09251'
'Local Shop 09252'
'Local Shop 09253'
'Local Shop 09254'
'Local Shop 09257'
'Local Shop 09265'
'Local Shop 09266'
'Local Shop 09268'
'Local Shop 09270'
'Local Shop 09271'
'Local Shop 09272'
'Local Shop 09275'
'Local Shop 09276'
'Local Shop 09277'
'Local Shop 09278'
'Local Shop 09279'
'Local Shop 09281'
'Local Shop 09282'
'Local Shop 09283'
'Local Shop 09286'
'Local Shop 09289'
'Local Shop 09290'
'Local Shop 09291'
'Local Shop 09292'
'Local Shop 09293'
'Local Shop 09295'
'Local Shop 09297'
'Local Shop 09298'
'Local Shop 09301'
'Local Shop 09302'
'Local Shop 09304'
'Local Shop 09307'
'Local Shop 09308'
'Local Shop 09309'
'Local Shop 09311'
'Local Shop 09315'
'Local Shop 09318'
'Local Shop 09319'
'Local Shop 09320'
'Local Shop 09322'
'Local Shop 09326'
'Local Shop 09327'
'Local Shop 09328'
'Local Shop 09331'
'Local Shop 09336'
'Local Shop 09339'
'Local Shop 09340'
'Local Shop 09342'
'Local Shop 09343'
'Local Shop 09346'
'Local Shop 09348'
'Local Shop 09349'
'Local Shop 09351'
'Local Shop 09354'
'Local Shop 09355'
'Local Shop 09356'
'Local Shop 09361'
'Local Shop 09362'
'Local Shop 09363'
'Local Shop 09366'
'Local Shop 09367'
'Local Shop 09368'
'Local Shop 09374'
'Local Shop 09378'
'Local Shop 09379'
'Local Shop 09382'
'Local Shop 09383'
'Local Shop 09385'
'Local Shop 09387'
'Local Shop 09388'
'Local Shop 09391'
'Local Shop 09394'
'Local Shop 09397'
'Local Shop 09399'
'Local Shop 09401'
'Local Shop 09402'
'Local Shop 09404'
'Local Shop 09405'
'Local Shop 09408'
'Local Shop 09412'
'Local Shop 09413'
'Local Shop 09414'
'Local Shop 09416'
'Local Shop 09421'
'Local Shop 09422'
'Local Shop 09424'
'Local Shop 09428'
'Local Shop 09429'
'Local Shop 09433'
'Local Shop 09435'
'Local Shop 09436'
'Local Shop 09437'
'Local Shop 09438'
'Local Shop 09441'
'Local Shop 09443'
'Local Shop 09445'
'Local Shop 09446'
'Local Shop 09449'
'Local Shop 09451'
'Local Shop 09453'
'Local Shop 09454'
'Local Shop 09460'
'Local Shop 09465'
'Local Shop 09470'
'Local Shop 09471'
'Local Shop 09472'
'Local Shop 09475'
'Local Shop 09476'
'Local Shop 09480'
'Local Shop 09482'
'Local Shop 09483'
'Local Shop 09487'
'Local Shop 09490'
'Local Shop 09491'
'Local Shop 09493'
'Local Shop 09496'
'Local Shop 09499'
'Local Shop 09500'
'Local Shop 09501'
'Local Shop 09502'
'Local Shop 09505'
'Local Shop 09507'
'Local Shop 09510'
'Local Shop 09512'
'Local Shop 09513'
'Local Shop 09514'
'Local Shop 09521'
'Local Shop 09525'
'Local Shop 09526'
'Local Shop 09528'
'Local Shop 09529'
'Local Shop 09537'
'Local Shop 09539'
'Local Shop 09540'
'Local Shop 09545'
'Local Shop 09546'
'Local Shop 09547'
'Local Shop 09549'
'Local Shop 09552'
'Local Shop 09553'
'Local Shop 09555'
'Local Shop 09561'
'Local Shop 09562'
'Local Shop 09565'
'Local Shop 09570'
'Local Shop 09572'
'Local Shop 09578'
'Local Shop 09580'
'Local Shop 09584'
'Local Shop 09586'
'Local Shop 09591'
'Local Shop 09592'
'Local Shop 09593'
'Local Shop 09594'
'Local Shop 09595'
'Local Shop 09596'
'Local Shop 09597'
'Local Shop 09599'
'Local Shop 09600'
'Local Shop 09603'
'Local Shop 09604'
'Local Shop 09605'
'Local Shop 09606'
'Local Shop 09608'
'Local Shop 09610'
'Local Shop 09617'
'Local Shop 09620'
'Local Shop 09623'
'Local Shop 09625'
'Local Shop 09626'
'Local Shop 09628'
'Local Shop 09629'
'Local Shop 09630'
'Local Shop 09631'
'Local Shop 09633'
'Local Shop 09635'
'Local Shop 09637'
'Local Shop 09639'
'Local Shop 09640'
'Local Shop 09641'
'Local Shop 09643'
'Local Shop 09645'
'Local Shop 09648'
'Local Shop 09650'
'Local Shop 09652'
'Local Shop 09656'
'Local Shop 09657'
'Local Shop 09658'
'Local Shop 09661'
'Local Shop 09664'
'Local Shop 09666'
'Local Shop 09668'
'Local Shop 09669'
'Local Shop 09670'
'Local Shop 09672'
'Local Shop 09674'
'Local Shop 09679'
'Local Shop 09680'
'Local Shop 09681'
'Local Shop 09683'
'Local Shop 09684'
'Local Shop 09685'
'Local Shop 09690'
'Local Shop 09691'
'Local Shop 09694'
'Local Shop 09696'
'Local Shop 09698'
'Local Shop 09700'
'Local Shop 09703'
'Local Shop 09704'
'Local Shop 09705'
'Local Shop 09708'
'Local Shop 09711'
'Local Shop 09714'
'Local Shop 09716'
'Local Shop 09717'
'Local Shop 09721'
'Local Shop 09723'
'Local Shop 09726'
'Local Shop 09727'
'Local Shop 09728'
'Local Shop 09733'
'Local Shop 09734'
'Local Shop 09736'
'Local Shop 09737'
'Local Shop 09739'
'Local Shop 09740'
'Local Shop 09741'
'Local Shop 09742'
'Local Shop 09744'
'Local Shop 09746'
'Local Shop 09747'
'Local Shop 09749'
'Local Shop 09752'
'Local Shop 09753'
'Local Shop 09754'
'Local Shop 09755'
'Local Shop 09757'
'Local Shop 09758'
'Local Shop 09759'
'Local Shop 09761'
'Local Shop 09762'
'Local Shop 09766'
'Local Shop 09772'
'Local Shop 09773'
'Local Shop 09775'
'Local Shop 09776'
'Local Shop 09780'
'Local Shop 09782'
'Local Shop 09783'
'Local Shop 09784'
'Local Shop 09785'
'Local Shop 09786'
'Local Shop 09787'
'Local Shop 09790'
'Local Shop 09792'
'Local Shop 09794'
'Local Shop 09795'
'Local Shop 09797'
'Local Shop 09798'
'Local Shop 09800'
'Local Shop 09801'
'Local Shop 09802'
'Local Shop 09804'
'Local Shop 09806'
'Local Shop 09807'
'Local Shop 09809'
'Local Shop 09810'
'Local Shop 09811'
'Local Shop 09814'
'Local Shop 09815'
'Local Shop 09818'
'Local Shop 09820'
'Local Shop 09823'
'Local Shop 09824'
'Local Shop 09825'
'Local Shop 09826'
'Local Shop 09827'
'Local Shop 09832'
'Local Shop 09833'
'Local Shop 09835'
'Local Shop 09836'
'Local Shop 09837'
'Local Shop 09840'
'Local Shop 09841'
'Local Shop 09842'
'Local Shop 09843'
'Local Shop 09845'
'Local Shop 09847'
'Local Shop 09851'
'Local Shop 09853'
'Local Shop 09855'
'Local Shop 09857'
'Local Shop 09858'
'Local Shop 09859'
'Local Shop 09863'
'Local Shop 09864'
'Local Shop 09867'
'Local Shop 09869'
'Local Shop 09872'
'Local Shop 09873'
'Local Shop 09876'
'Local Shop 09877'
'Local Shop 09879'
'Local Shop 09880'
'Local Shop 09881'
'Local Shop 09885'
'Local Shop 09886'
'Local Shop 09887'
'Local Shop 09888'
'Local Shop 09890'
'Local Shop 09893'
'Local Shop 09895'
'Local Shop 09896'
'Local Shop 09901'
'Local Shop 09903'
'Local Shop 09904'
'Local Shop 09905'
'Local Shop 09910'
'Local Shop 09911'
'Local Shop 09915'
'Local Shop 09917'
'Local Shop 09919'
'Local Shop 09920'
'Local Shop 09922'
'Local Shop 09923'
'Local Shop 09928'
'Local Shop 09931'
'Local Shop 09933'
'Local Shop 09934'
'Local Shop 09935'
'Local Shop 09936'
'Local Shop 09940'
'Local Shop 09941'
'Local Shop 09942'
'Local Shop 09943'
'Local Shop 09944'
'Local Shop 09945'
'Local Shop 09946'
'Local Shop 09947'
'Local Shop 09949'
'Local Shop 09950'
'Local Shop 09953'
'Local Shop 09955'
'Local Shop 09956'
'Local Shop 09959'
'Local Shop 09961'
'Local Shop 09962'
'Local Shop 09964'
'Local Shop 09965'
'Local Shop 09967'
'Local Shop 09968'
'Local Shop 09970'
'Local Shop 09973'
'Local Shop 09975'
'Local Shop 09976'
'Local Shop 09977'
'Local Shop 09978'
'Local Shop 09979'
'Local Shop 09982'
'Local Shop 09985'
'Local Shop 09986'
'Local Shop 09989'
'Local Shop 09991'
'Local Shop 09992'
'Local Shop 09997'
'Local Shop 10000'
'Metro Market'
'Metro Markets'
'OSCAR'
'Oscar'
'Oscar Grand Stores'
'Seoudi'
'Seoudi Market'
'Spineys'
'Spinneys'
'abou zekry'
'carrefour'
'hyperone'
'kheir zaman'
'metro'
'seoudi supermarket'
'spinneys cairo'
'أبو زكري'
'اوسكار'
'خير زمان'
'سبينيز'
'سعودي'
'كارفور'
'مترو'
'هايبر وان'

=== Unique anchor names ===
'  Abdeen Palace Edge  '
'  Bab Al Louq Corner  '
'  Boulaq Edge  '
'  Cleopatra Plaza  '
'  Coptic Cairo Plaza  '
'  Dar El Salaam  '
'  Dokki Market  '
'  El Agouza Riverside  '
'  El Demerdash Hospital Edge  '
'  El Mokattam Plateau  '
'  El Mosky Quarter  '
'  El Rehab Gate One  '
'  El Sayeda Aisha  '
'  Embaba Crossing  '
'  Fifth Settlement North  '
'  Maadi Corniche  '
'  Maadi Sarayat  '
'  Maasara Crossing  '
'  Madinet Nasr Eighth Zone  '
'  Mall of Egypt Gate  '
'  Mokattam Sector One  '
'  Nasr City Hub  '
'  Nozha Promenade  '
'  Opera Square  '
'  Police Academy Strip  '
'  Ramses Crossing  '
'  Ring Road West  '
'  Saint Fatima Hub  '
'  Sharkawi Plaza  '
'  Sherif Street  '
'  Shubra El Kheima Centre  '
'  Shubra North  '
'  Tagamoa El Saba  '
'AIN SHAMS PLAZA'
'AL AHLY STADIUM'
'Abbasiya Junction'
'Al Ghouriya Strip'
'American University Gate'
'Ataba Square'
'Autostrad Corner'
'BAB ZUWEILA APPROACH'
'BAHTEEM CROSSING'
'CITY STARS MALL'
'Cairo Festival City'
'Cairo Stadium'
'Demerdash Plaza'
'EL HUSSEIN SQUARE'
'EL MARG HUB'
'EL NOZHA EL GEDIDA'
'EL OBOUR HUB'
'EL REHAB GATE TWO'
'EL SAHEL JUNCTION'
'EL SAWAH CORNER'
'El Hadaba El Wosta'
'El Maadi Degla'
'El Salam City'
'FIFTH SETTLEMENT SOUTH'
'FUSTAT PARK EDGE'
'GARBIYA PLAZA'
'GIZA PYRAMIDS GATE'
'Garden City Walk'
'HELIOPOLIS SQUARE'
'Helwan Centre'
'IMBABA AIRPORT STRIP'
'KOBRI EL QUBBA'
'KORBA QUARTER'
'Kasr El Aini Strip'
'Khan El Khalili Approach'
'MAADI CORNISH'
'MADINET NASR TENTH ZONE'
'MOHANDESSIN CENTRE'
'MOKATTAM SECTOR SIX'
'Madinaty Promenade'
'Manial Bridge'
'Manial Riverbank'
'Mar Mina Plaza'
'Mit Okba Plaza'
'Mokattam Heights'
'New Cairo Tagamoa'
'Old Cairo Babylon'
'QASR EL NILE'
'RAMSES HILTON PLAZA'
'RING ROAD NORTH'
'Rabaa Square'
'Ring Road East'
'Rod El Farag Bridge'
'Roxy Square'
'SALAH SALEM STRIP'
'SAYEDA ZEINAB PLAZA'
'SHERATON HELIOPOLIS'
'Sakakini Approach'
'Shoubra Mazallat'
'Soliman Pasha Corner'
'TAGAMOA FIRST'
'TAHRIR SQUARE PLAZA'
'TALAAT HARB PLAZA'
'TORA EDGE'
'Tagamoa Third'
'Triumph Square'
'Zamalek Riverside'

Total shops: 10000
Total anchors: 100
0:49
0:49
Bash cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
shops = gpd.read_file('cairo_retail.gpkg', layer='shops')
anchors = gpd.read_file('cairo_retail.gpkg', layer='anchors')
print('Shops CRS:', shops.crs)
print('Anchors CRS:', anchors.crs)
# Check if it's projected (metric)
print('Shops geometry type:', shops.geometry.iloc[0])
print('Anchors geometry type:', anchors.geometry.iloc[0])
"
Shops CRS: EPSG:22992
Anchors CRS: EPSG:22992
Shops geometry type: POINT (637478.0584879243 814957.3976758636)
Anchors geometry type: POINT (635109.3426487555 805565.9093887237)
1:04
1:04
Bash cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
shops = gpd.read_file('cairo_retail.gpkg', layer='shops')
anchors = gpd.read_file('cairo_retail.gpkg', layer='anchors')

# Check which shops have chain names vs local shops
chain_shops = shops[~shops['raw_name'].str.startswith('Local Shop')]
print('=== Chain-branded shops ===')
print(chain_shops[['shop_id', 'raw_name']].to_string())
print(f'Count: {len(chain_shops)}')
"
=== Chain-branded shops ===
     shop_id            raw_name
0     S00001               OSCAR
5     S00006                مترو
7     S00008           Carrefour
9     S00010           Carrefour
11    S00012               سعودي
15    S00016            Spinneys
20    S00021     Carrefour Egypt
21    S00022           carrefour
25    S00026       Seoudi Market
27    S00028           Hyper One
28    S00029            أبو زكري
31    S00032              سبينيز
38    S00039       Metro Markets
41    S00042       Seoudi Market
42    S00043  seoudi supermarket
43    S00044              كارفور
44    S00045       Seoudi Market
45    S00046         Kheir Zaman
46    S00047                مترو
50    S00051            Spinneys
53    S00054              اوسكار
56    S00057     Carrefour Egypt
57    S00058             Spineys
59    S00060              اوسكار
60    S00061            خير زمان
61    S00062              Seoudi
63    S00064            Spinneys
64    S00065         kheir zaman
65    S00066       Metro Markets
67    S00068        Metro Market
69    S00070     Carrefour Egypt
72    S00073           Carrefour
73    S00074        Metro Market
77    S00078              Seoudi
82    S00083           Abu Zekry
86    S00087        Metro Market
88    S00089          abou zekry
93    S00094  Oscar Grand Stores
96    S00097               metro
97    S00098           Carrefour
98    S00099          abou zekry
99    S00100  Oscar Grand Stores
100   S00101           Carrefour
101   S00102              Seoudi
106   S00107           Carrefour
107   S00108               metro
108   S00109              Seoudi
111   S00112                مترو
113   S00114           هايبر وان
114   S00115               OSCAR
115   S00116             Spineys
116   S00117           Abu Zikri
117   S00118  seoudi supermarket
121   S00122  Oscar Grand Stores
122   S00123      spinneys cairo
123   S00124           Abu Zikri
125   S00126             Spineys
127   S00128               OSCAR
128   S00129               metro
129   S00130       Metro Markets
130   S00131            خير زمان
131   S00132            hyperone
132   S00133             Spineys
133   S00134       Seoudi Market
139   S00140               Oscar
140   S00141       Seoudi Market
144   S00145               OSCAR
146   S00147        Metro Market
147   S00148  Oscar Grand Stores
148   S00149         Khair Zaman
153   S00154               Oscar
154   S00155            خير زمان
155   S00156               metro
159   S00160           Abu Zikri
162   S00163           Abu Zikri
167   S00168             Spineys
172   S00173  seoudi supermarket
173   S00174            HyperOne
174   S00175     Carrefour Egypt
176   S00177           Abu Zikri
177   S00178           Abu Zekry
178   S00179            أبو زكري
179   S00180        Metro Market
182   S00183              كارفور
185   S00186           هايبر وان
188   S00189            خير زمان
192   S00193            أبو زكري
195   S00196              كارفور
198   S00199           carrefour
203   S00204              سبينيز
206   S00207           Carrefour
207   S00208             Spineys
211   S00212  seoudi supermarket
214   S00215           Abu Zekry
217   S00218                مترو
218   S00219           هايبر وان
219   S00220            Spinneys
220   S00221              اوسكار
222   S00223               سعودي
223   S00224         Khair Zaman
225   S00226       Seoudi Market
231   S00232           Carrefour
233   S00234       Seoudi Market
234   S00235           Abu Zekry
235   S00236        Metro Market
236   S00237       Metro Markets
237   S00238         Kheir Zaman
239   S00240        Metro Market
242   S00243         Khair Zaman
246   S00247            خير زمان
247   S00248              اوسكار
248   S00249  Oscar Grand Stores
251   S00252           هايبر وان
252   S00253            hyperone
253   S00254           Hyper One
254   S00255           هايبر وان
255   S00256           هايبر وان
256   S00257         Kheir Zaman
260   S00261            خير زمان
262   S00263               Oscar
264   S00265               سعودي
266   S00267         Kheir Zaman
269   S00270              اوسكار
271   S00272       Seoudi Market
272   S00273               OSCAR
273   S00274                مترو
276   S00277             Spineys
280   S00281               OSCAR
283   S00284  Oscar Grand Stores
284   S00285       Metro Markets
286   S00287            أبو زكري
287   S00288            Spinneys
289   S00290              Seoudi
290   S00291               metro
291   S00292               metro
292   S00293      spinneys cairo
293   S00294         kheir zaman
295   S00296       Seoudi Market
296   S00297           Carrefour
298   S00299            hyperone
301   S00302        Metro Market
303   S00304            أبو زكري
307   S00308          abou zekry
309   S00310            hyperone
315   S00316           Carrefour
316   S00317         Kheir Zaman
317   S00318           Abu Zikri
320   S00321        Metro Market
323   S00324            خير زمان
325   S00326            hyperone
326   S00327            خير زمان
328   S00329  Oscar Grand Stores
329   S00330               OSCAR
330   S00331            Spinneys
331   S00332                مترو
332   S00333            hyperone
335   S00336                مترو
338   S00339  Oscar Grand Stores
339   S00340           Abu Zekry
340   S00341        Metro Market
342   S00343            Spinneys
343   S00344            أبو زكري
344   S00345               Oscar
347   S00348           Abu Zekry
348   S00349       Metro Markets
351   S00352            hyperone
354   S00355              كارفور
355   S00356           Hyper One
357   S00358            hyperone
358   S00359            hyperone
359   S00360              سبينيز
363   S00364     Carrefour Egypt
367   S00368           Carrefour
368   S00369              اوسكار
369   S00370            خير زمان
370   S00371              اوسكار
372   S00373                مترو
373   S00374               سعودي
376   S00377           Hyper One
380   S00381              اوسكار
381   S00382           Abu Zikri
382   S00383              Seoudi
383   S00384        Metro Market
385   S00386            hyperone
387   S00388              Seoudi
388   S00389           Abu Zikri
389   S00390           Carrefour
390   S00391           هايبر وان
392   S00393            hyperone
395   S00396              اوسكار
397   S00398             Spineys
398   S00399            Spinneys
400   S00401            خير زمان
401   S00402           Hyper One
403   S00404               Oscar
404   S00405               metro
405   S00406           Abu Zekry
406   S00407              اوسكار
410   S00411          abou zekry
411   S00412           Abu Zekry
413   S00414         kheir zaman
414   S00415     Carrefour Egypt
415   S00416               OSCAR
418   S00419            hyperone
420   S00421         kheir zaman
421   S00422         Khair Zaman
424   S00425  Oscar Grand Stores
425   S00426              اوسكار
427   S00428                مترو
429   S00430             Spineys
430   S00431              اوسكار
435   S00436         kheir zaman
437   S00438            Spinneys
439   S00440              كارفور
443   S00444            hyperone
444   S00445  seoudi supermarket
445   S00446               metro
448   S00449              Seoudi
450   S00451       Metro Markets
452   S00453         Kheir Zaman
453   S00454            خير زمان
457   S00458         kheir zaman
460   S00461  seoudi supermarket
463   S00464           Abu Zikri
466   S00467            HyperOne
468   S00469         Khair Zaman
474   S00475              Seoudi
475   S00476          abou zekry
481   S00482             Spineys
484   S00485         Khair Zaman
489   S00490              Seoudi
491   S00492        Metro Market
494   S00495         Kheir Zaman
496   S00497              كارفور
499   S00500      spinneys cairo
500   S00501            Spinneys
503   S00504               metro
507   S00508            أبو زكري
509   S00510                مترو
511   S00512            خير زمان
518   S00519               Oscar
521   S00522              اوسكار
522   S00523           Abu Zikri
525   S00526         kheir zaman
532   S00533               metro
533   S00534         Khair Zaman
534   S00535               Oscar
536   S00537     Carrefour Egypt
538   S00539         Kheir Zaman
539   S00540            HyperOne
541   S00542       Metro Markets
542   S00543               OSCAR
543   S00544              Seoudi
545   S00546         kheir zaman
552   S00553              Seoudi
553   S00554           Carrefour
554   S00555               Oscar
557   S00558     Carrefour Egypt
558   S00559            Spinneys
563   S00564               OSCAR
564   S00565           Hyper One
568   S00569        Metro Market
571   S00572            hyperone
574   S00575     Carrefour Egypt
575   S00576           Carrefour
579   S00580            أبو زكري
581   S00582           Carrefour
583   S00584           Abu Zekry
586   S00587  Oscar Grand Stores
591   S00592       Metro Markets
594   S00595               metro
597   S00598           هايبر وان
598   S00599         Khair Zaman
599   S00600       Metro Markets
604   S00605  Oscar Grand Stores
605   S00606              سبينيز
606   S00607              سبينيز
608   S00609            HyperOne
613   S00614      spinneys cairo
619   S00620               سعودي
620   S00621        Metro Market
621   S00622               سعودي
624   S00625            hyperone
625   S00626               metro
626   S00627           Hyper One
627   S00628           Abu Zikri
628   S00629              سبينيز
629   S00630         kheir zaman
630   S00631               Oscar
631   S00632            Spinneys
632   S00633              Seoudi
633   S00634           Hyper One
634   S00635             Spineys
635   S00636           carrefour
637   S00638              Seoudi
638   S00639  seoudi supermarket
643   S00644      spinneys cairo
644   S00645          abou zekry
645   S00646           Abu Zikri
646   S00647               metro
648   S00649      spinneys cairo
650   S00651                مترو
651   S00652            hyperone
653   S00654            أبو زكري
654   S00655            Spinneys
655   S00656              Seoudi
656   S00657           Hyper One
667   S00668          abou zekry
669   S00670         kheir zaman
670   S00671            Spinneys
671   S00672           هايبر وان
676   S00677           Abu Zikri
678   S00679           هايبر وان
681   S00682            HyperOne
683   S00684               OSCAR
684   S00685               OSCAR
685   S00686  Oscar Grand Stores
688   S00689        Metro Market
690   S00691           Abu Zekry
691   S00692      spinneys cairo
693   S00694              سبينيز
695   S00696  seoudi supermarket
697   S00698               سعودي
699   S00700              سبينيز
700   S00701               سعودي
701   S00702           Hyper One
702   S00703              كارفور
703   S00704         Khair Zaman
704   S00705              سبينيز
705   S00706              سبينيز
708   S00709            أبو زكري
712   S00713            hyperone
714   S00715     Carrefour Egypt
716   S00717             Spineys
722   S00723               OSCAR
723   S00724            أبو زكري
724   S00725              كارفور
725   S00726         kheir zaman
726   S00727      spinneys cairo
727   S00728            أبو زكري
728   S00729     Carrefour Egypt
729   S00730               metro
731   S00732            HyperOne
732   S00733              اوسكار
737   S00738      spinneys cairo
739   S00740              سبينيز
740   S00741       Seoudi Market
741   S00742           Carrefour
742   S00743               سعودي
743   S00744  seoudi supermarket
746   S00747           carrefour
747   S00748               سعودي
749   S00750               OSCAR
751   S00752     Carrefour Egypt
752   S00753       Seoudi Market
753   S00754            HyperOne
755   S00756           Hyper One
757   S00758           هايبر وان
758   S00759      spinneys cairo
759   S00760               metro
760   S00761           carrefour
762   S00763               سعودي
763   S00764             Spineys
765   S00766               Oscar
766   S00767             Spineys
767   S00768               OSCAR
768   S00769              Seoudi
769   S00770            hyperone
770   S00771           هايبر وان
771   S00772           Abu Zekry
774   S00775           Carrefour
776   S00777     Carrefour Egypt
779   S00780         kheir zaman
780   S00781           Abu Zekry
782   S00783           Carrefour
783   S00784              كارفور
784   S00785                مترو
787   S00788               سعودي
792   S00793       Seoudi Market
795   S00796            أبو زكري
797   S00798            Spinneys
798   S00799         Kheir Zaman
801   S00802               OSCAR
802   S00803              سبينيز
804   S00805               سعودي
805   S00806         Khair Zaman
806   S00807               Oscar
808   S00809               OSCAR
810   S00811           Abu Zekry
816   S00817         Khair Zaman
817   S00818     Carrefour Egypt
819   S00820              اوسكار
821   S00822             Spineys
822   S00823     Carrefour Egypt
824   S00825             Spineys
827   S00828        Metro Market
829   S00830      spinneys cairo
830   S00831      spinneys cairo
831   S00832            خير زمان
832   S00833       Seoudi Market
833   S00834               OSCAR
836   S00837        Metro Market
838   S00839               metro
839   S00840          abou zekry
841   S00842            Spinneys
842   S00843            Spinneys
843   S00844           carrefour
844   S00845     Carrefour Egypt
845   S00846               Oscar
846   S00847            أبو زكري
847   S00848       Metro Markets
851   S00852           carrefour
853   S00854           Abu Zikri
855   S00856            Spinneys
857   S00858       Metro Markets
859   S00860       Metro Markets
860   S00861     Carrefour Egypt
861   S00862       Seoudi Market
864   S00865          abou zekry
866   S00867            خير زمان
867   S00868  Oscar Grand Stores
868   S00869              اوسكار
871   S00872            Spinneys
872   S00873           Carrefour
873   S00874          abou zekry
876   S00877  seoudi supermarket
877   S00878            HyperOne
882   S00883            hyperone
883   S00884        Metro Market
884   S00885             Spineys
885   S00886            hyperone
886   S00887           هايبر وان
888   S00889           Abu Zikri
891   S00892          abou zekry
893   S00894              سبينيز
894   S00895         kheir zaman
895   S00896           هايبر وان
896   S00897  seoudi supermarket
898   S00899            HyperOne
903   S00904                مترو
904   S00905               Oscar
905   S00906        Metro Market
906   S00907  Oscar Grand Stores
907   S00908         Khair Zaman
908   S00909             Spineys
910   S00911       Seoudi Market
911   S00912            Spinneys
914   S00915           هايبر وان
915   S00916        Metro Market
919   S00920               metro
922   S00923            hyperone
923   S00924            خير زمان
924   S00925         Kh
[... truncated ...]Zekry
9099  S09100               metro
9100  S09101         kheir zaman
9101  S09102           هايبر وان
9102  S09103               metro
9104  S09105               Oscar
9105  S09106           Hyper One
9106  S09107           Abu Zekry
9108  S09109     Carrefour Egypt
9110  S09111         kheir zaman
9113  S09114            أبو زكري
9115  S09116  Oscar Grand Stores
9118  S09119          abou zekry
9119  S09120            خير زمان
9120  S09121              سبينيز
9121  S09122            hyperone
9123  S09124           Hyper One
9126  S09127         kheir zaman
9131  S09132               سعودي
9134  S09135            Spinneys
9135  S09136               metro
9136  S09137              Seoudi
9143  S09144               OSCAR
9145  S09146               metro
9149  S09150           carrefour
9150  S09151  seoudi supermarket
9151  S09152       Seoudi Market
9154  S09155              كارفور
9156  S09157           Carrefour
9157  S09158         Kheir Zaman
9159  S09160       Seoudi Market
9161  S09162         Kheir Zaman
9162  S09163            hyperone
9166  S09167            أبو زكري
9167  S09168               metro
9168  S09169            خير زمان
9169  S09170           Abu Zekry
9170  S09171               metro
9172  S09173         kheir zaman
9173  S09174         Khair Zaman
9175  S09176            أبو زكري
9177  S09178           هايبر وان
9178  S09179                مترو
9180  S09181              كارفور
9185  S09186         kheir zaman
9186  S09187              Seoudi
9188  S09189      spinneys cairo
9191  S09192       Metro Markets
9192  S09193           Abu Zekry
9196  S09197              سبينيز
9199  S09200              اوسكار
9202  S09203         Khair Zaman
9203  S09204  seoudi supermarket
9204  S09205           Abu Zikri
9210  S09211                مترو
9214  S09215       Metro Markets
9217  S09218            Spinneys
9221  S09222       Seoudi Market
9227  S09228              سبينيز
9228  S09229         Kheir Zaman
9229  S09230            خير زمان
9230  S09231           Abu Zekry
9232  S09233               OSCAR
9234  S09235     Carrefour Egypt
9235  S09236            Spinneys
9236  S09237  Oscar Grand Stores
9237  S09238      spinneys cairo
9239  S09240           Carrefour
9243  S09244           Abu Zekry
9244  S09245           Abu Zekry
9246  S09247        Metro Market
9248  S09249            أبو زكري
9254  S09255           Abu Zikri
9255  S09256               Oscar
9257  S09258     Carrefour Egypt
9258  S09259            خير زمان
9259  S09260           هايبر وان
9260  S09261       Seoudi Market
9261  S09262                مترو
9262  S09263              سبينيز
9263  S09264         Khair Zaman
9266  S09267           Abu Zekry
9268  S09269     Carrefour Egypt
9272  S09273       Seoudi Market
9273  S09274            hyperone
9279  S09280          abou zekry
9283  S09284            أبو زكري
9284  S09285              Seoudi
9286  S09287         Khair Zaman
9287  S09288            Spinneys
9293  S09294      spinneys cairo
9295  S09296            أبو زكري
9298  S09299             Spineys
9299  S09300     Carrefour Egypt
9302  S09303  seoudi supermarket
9304  S09305              اوسكار
9305  S09306         Kheir Zaman
9309  S09310  seoudi supermarket
9311  S09312               OSCAR
9312  S09313         Khair Zaman
9313  S09314        Metro Market
9315  S09316               OSCAR
9316  S09317            خير زمان
9320  S09321           carrefour
9322  S09323         kheir zaman
9323  S09324            خير زمان
9324  S09325         Kheir Zaman
9328  S09329       Seoudi Market
9329  S09330              اوسكار
9331  S09332           Abu Zikri
9332  S09333               OSCAR
9333  S09334           Hyper One
9334  S09335               Oscar
9336  S09337               سعودي
9337  S09338         Khair Zaman
9340  S09341              كارفور
9343  S09344           carrefour
9344  S09345           Abu Zikri
9346  S09347           Hyper One
9349  S09350           carrefour
9351  S09352               سعودي
9352  S09353     Carrefour Egypt
9356  S09357             Spineys
9357  S09358       Metro Markets
9358  S09359        Metro Market
9359  S09360           Abu Zikri
9363  S09364         Khair Zaman
9364  S09365            أبو زكري
9368  S09369        Metro Market
9369  S09370         Kheir Zaman
9370  S09371               metro
9371  S09372               metro
9372  S09373           Hyper One
9374  S09375           Abu Zikri
9375  S09376       Metro Markets
9376  S09377              كارفور
9379  S09380               سعودي
9380  S09381            أبو زكري
9383  S09384           Carrefour
9385  S09386           Abu Zikri
9388  S09389           Abu Zekry
9389  S09390               Oscar
9391  S09392           Abu Zekry
9392  S09393           Abu Zekry
9394  S09395           هايبر وان
9395  S09396       Seoudi Market
9397  S09398       Seoudi Market
9399  S09400         Kheir Zaman
9402  S09403               سعودي
9405  S09406     Carrefour Egypt
9406  S09407         kheir zaman
9408  S09409         Kheir Zaman
9409  S09410      spinneys cairo
9410  S09411         Kheir Zaman
9414  S09415         kheir zaman
9416  S09417        Metro Market
9417  S09418            HyperOne
9418  S09419      spinneys cairo
9419  S09420          abou zekry
9422  S09423           Abu Zikri
9424  S09425         kheir zaman
9425  S09426            HyperOne
9426  S09427           carrefour
9429  S09430         kheir zaman
9430  S09431        Metro Market
9431  S09432     Carrefour Egypt
9433  S09434  seoudi supermarket
9438  S09439      spinneys cairo
9439  S09440         kheir zaman
9441  S09442               OSCAR
9443  S09444              سبينيز
9446  S09447        Metro Market
9447  S09448         Khair Zaman
9449  S09450     Carrefour Egypt
9451  S09452              سبينيز
9454  S09455         Khair Zaman
9455  S09456            خير زمان
9456  S09457           Hyper One
9457  S09458           carrefour
9458  S09459              Seoudi
9460  S09461           Abu Zikri
9461  S09462          abou zekry
9462  S09463            خير زمان
9463  S09464           هايبر وان
9465  S09466         kheir zaman
9466  S09467        Metro Market
9467  S09468  Oscar Grand Stores
9468  S09469           Hyper One
9472  S09473            أبو زكري
9473  S09474            خير زمان
9476  S09477            Spinneys
9477  S09478            HyperOne
9478  S09479             Spineys
9480  S09481         Khair Zaman
9483  S09484     Carrefour Egypt
9484  S09485  seoudi supermarket
9485  S09486               metro
9487  S09488         kheir zaman
9488  S09489               metro
9491  S09492              Seoudi
9493  S09494           Hyper One
9494  S09495         Khair Zaman
9496  S09497            HyperOne
9497  S09498            خير زمان
9502  S09503            أبو زكري
9503  S09504           Abu Zekry
9505  S09506               metro
9507  S09508                مترو
9508  S09509              كارفور
9510  S09511              سبينيز
9514  S09515              اوسكار
9515  S09516        Metro Market
9516  S09517  seoudi supermarket
9517  S09518           Hyper One
9518  S09519           Abu Zikri
9519  S09520               سعودي
9521  S09522            خير زمان
9522  S09523             Spineys
9523  S09524           Abu Zekry
9526  S09527           هايبر وان
9529  S09530            أبو زكري
9530  S09531           Abu Zekry
9531  S09532           carrefour
9532  S09533            hyperone
9533  S09534             Spineys
9534  S09535              اوسكار
9535  S09536      spinneys cairo
9537  S09538           carrefour
9540  S09541                مترو
9541  S09542            أبو زكري
9542  S09543            hyperone
9543  S09544               metro
9547  S09548         Kheir Zaman
9549  S09550      spinneys cairo
9550  S09551       Seoudi Market
9553  S09554            خير زمان
9555  S09556              كارفور
9556  S09557              كارفور
9557  S09558     Carrefour Egypt
9558  S09559              كارفور
9559  S09560      spinneys cairo
9562  S09563              سبينيز
9563  S09564         Khair Zaman
9565  S09566         kheir zaman
9566  S09567           Abu Zekry
9567  S09568     Carrefour Egypt
9568  S09569     Carrefour Egypt
9570  S09571          abou zekry
9572  S09573       Metro Markets
9573  S09574             Spineys
9574  S09575        Metro Market
9575  S09576            خير زمان
9576  S09577       Seoudi Market
9578  S09579           هايبر وان
9580  S09581             Spineys
9581  S09582               OSCAR
9582  S09583             Spineys
9584  S09585        Metro Market
9586  S09587       Seoudi Market
9587  S09588              Seoudi
9588  S09589            خير زمان
9589  S09590        Metro Market
9597  S09598  Oscar Grand Stores
9600  S09601               OSCAR
9601  S09602           Hyper One
9606  S09607         kheir zaman
9608  S09609            Spinneys
9610  S09611               Oscar
9611  S09612               Oscar
9612  S09613       Seoudi Market
9613  S09614      spinneys cairo
9614  S09615           Abu Zekry
9615  S09616               metro
9617  S09618           Abu Zekry
9618  S09619       Metro Markets
9620  S09621           Hyper One
9621  S09622           Abu Zikri
9623  S09624                مترو
9626  S09627             Spineys
9631  S09632         Khair Zaman
9633  S09634           carrefour
9635  S09636            أبو زكري
9637  S09638            Spinneys
9641  S09642              كارفور
9643  S09644               OSCAR
9645  S09646  Oscar Grand Stores
9646  S09647         Khair Zaman
9648  S09649              سبينيز
9650  S09651           Carrefour
9652  S09653         Kheir Zaman
9653  S09654  Oscar Grand Stores
9654  S09655         kheir zaman
9658  S09659               metro
9659  S09660            أبو زكري
9661  S09662         Kheir Zaman
9662  S09663           Abu Zekry
9664  S09665              Seoudi
9666  S09667      spinneys cairo
9670  S09671              Seoudi
9672  S09673            hyperone
9674  S09675            HyperOne
9675  S09676  seoudi supermarket
9676  S09677       Seoudi Market
9677  S09678  seoudi supermarket
9681  S09682  Oscar Grand Stores
9685  S09686             Spineys
9686  S09687              سبينيز
9687  S09688          abou zekry
9688  S09689         Khair Zaman
9691  S09692         kheir zaman
9692  S09693              سبينيز
9694  S09695           Carrefour
9696  S09697           carrefour
9698  S09699              كارفور
9700  S09701              Seoudi
9701  S09702            hyperone
9705  S09706               metro
9706  S09707  seoudi supermarket
9708  S09709               سعودي
9709  S09710           Hyper One
9711  S09712            خير زمان
9712  S09713          abou zekry
9714  S09715           Hyper One
9717  S09718         kheir zaman
9718  S09719       Metro Markets
9719  S09720        Metro Market
9721  S09722         Kheir Zaman
9723  S09724           Hyper One
9724  S09725            خير زمان
9728  S09729        Metro Market
9729  S09730  Oscar Grand Stores
9730  S09731               OSCAR
9731  S09732  Oscar Grand Stores
9734  S09735         kheir zaman
9737  S09738         kheir zaman
9742  S09743               سعودي
9744  S09745            خير زمان
9747  S09748            HyperOne
9749  S09750       Metro Markets
9750  S09751            خير زمان
9755  S09756               metro
9759  S09760           Hyper One
9762  S09763               metro
9763  S09764            أبو زكري
9764  S09765           هايبر وان
9766  S09767  Oscar Grand Stores
9767  S09768       Seoudi Market
9768  S09769           carrefour
9769  S09770              كارفور
9770  S09771            Spinneys
9773  S09774              اوسكار
9776  S09777              كارفور
9777  S09778               metro
9778  S09779         Khair Zaman
9780  S09781                مترو
9787  S09788  Oscar Grand Stores
9788  S09789           Abu Zekry
9790  S09791              كارفور
9792  S09793            hyperone
9795  S09796           Abu Zekry
9798  S09799              كارفور
9802  S09803           هايبر وان
9804  S09805         Kheir Zaman
9807  S09808            Spinneys
9811  S09812            hyperone
9812  S09813         kheir zaman
9815  S09816          abou zekry
9816  S09817  Oscar Grand Stores
9818  S09819            Spinneys
9820  S09821      spinneys cairo
9821  S09822           Abu Zikri
9827  S09828           Carrefour
9828  S09829           Hyper One
9829  S09830         Khair Zaman
9830  S09831       Metro Markets
9833  S09834            خير زمان
9837  S09838            خير زمان
9838  S09839     Carrefour Egypt
9843  S09844        Metro Market
9845  S09846           Hyper One
9847  S09848     Carrefour Egypt
9848  S09849           Abu Zikri
9849  S09850              اوسكار
9851  S09852                مترو
9853  S09854         Khair Zaman
9855  S09856            hyperone
9859  S09860               سعودي
9860  S09861       Seoudi Market
9861  S09862           Abu Zekry
9864  S09865       Metro Markets
9865  S09866            خير زمان
9867  S09868      spinneys cairo
9869  S09870           Hyper One
9870  S09871            HyperOne
9873  S09874            أبو زكري
9874  S09875               سعودي
9877  S09878              اوسكار
9881  S09882               سعودي
9882  S09883              كارفور
9883  S09884           Hyper One
9888  S09889             Spineys
9890  S09891             Spineys
9891  S09892                مترو
9893  S09894             Spineys
9896  S09897  seoudi supermarket
9897  S09898         Khair Zaman
9898  S09899               metro
9899  S09900              اوسكار
9901  S09902       Metro Markets
9905  S09906                مترو
9906  S09907          abou zekry
9907  S09908      spinneys cairo
9908  S09909  Oscar Grand Stores
9911  S09912            أبو زكري
9912  S09913           carrefour
9913  S09914       Seoudi Market
9915  S09916        Metro Market
9917  S09918         kheir zaman
9920  S09921            hyperone
9923  S09924              كارفور
9924  S09925               OSCAR
9925  S09926            HyperOne
9926  S09927               metro
9928  S09929           Abu Zikri
9929  S09930            Spinneys
9931  S09932         Khair Zaman
9936  S09937            hyperone
9937  S09938           Abu Zikri
9938  S09939         kheir zaman
9947  S09948           Abu Zikri
9950  S09951  seoudi supermarket
9951  S09952               metro
9953  S09954     Carrefour Egypt
9956  S09957           Carrefour
9957  S09958               OSCAR
9959  S09960             Spineys
9962  S09963       Seoudi Market
9965  S09966           carrefour
9968  S09969             Spineys
9970  S09971               metro
9971  S09972          abou zekry
9973  S09974       Metro Markets
9979  S09980              كارفور
9980  S09981          abou zekry
9982  S09983       Metro Markets
9983  S09984         kheir zaman
9986  S09987              Seoudi
9987  S09988                مترو
9989  S09990       Metro Markets
9992  S09993            Spinneys
9993  S09994      spinneys cairo
9994  S09995        Metro Market
9995  S09996               OSCAR
9997  S09998              كارفور
9998  S09999            أبو زكري
Count: 4997
1:44
1:44
Write /work/solve.py
content · 228 lines · py
#!/usr/bin/env python3
"""
Cairo retail density readout.
For each anchor: 5 nearest shops (with distance, within_1km flag),
plus a 5x3 distance matrix to the anchor's 3 closest sibling anchors.
"""

import json
import numpy as np
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point

# ---------------------------------------------------------------------------
# 1. Load data
# ---------------------------------------------------------------------------
shops = gpd.read_file("/work/cairo_retail.gpkg", layer="shops")
anchors = gpd.read_file("/work/cairo_retail.gpkg", layer="anchors")

# Both are EPSG:22992 (metric), so distances are in metres.
assert shops.crs is not None and shops.crs.to_epsg() == 22992
assert anchors.crs is not None and anchors.crs.to_epsg() == 22992

# ---------------------------------------------------------------------------
# 2. Build normalised name mapping for shops
# ---------------------------------------------------------------------------
# The rule: same shop_id always carries the same normalised_name,
# and all shops belonging to one chain share a single canonical spelling.
# Local Shop XXXXX entries are kept as-is (they are individual, not chains).

# Build a mapping from raw_name -> normalised_name
raw_to_normalised = {}

# Chain normalisation rules (canonical spelling)
chain_rules = {
    # Oscar group
    "OSCAR": "Oscar",
    "Oscar": "Oscar",
    "Oscar Grand Stores": "Oscar",
    "اوسكار": "Oscar",
    # Carrefour group
    "Carrefour": "Carrefour",
    "Carrefour Egypt": "Carrefour",
    "carrefour": "Carrefour",
    "كارفور": "Carrefour",
    # Hyper One group
    "Hyper One": "Hyper One",
    "HyperOne": "Hyper One",
    "hyperone": "Hyper One",
    "هايبر وان": "Hyper One",
    # Metro group
    "Metro Market": "Metro",
    "Metro Markets": "Metro",
    "metro": "Metro",
    "مترو": "Metro",
    # Spinneys group
    "Spinneys": "Spinneys",
    "Spineys": "Spinneys",
    "spinneys cairo": "Spinneys",
    "سبينيز": "Spinneys",
    # Seoudi group
    "Seoudi": "Seoudi",
    "Seoudi Market": "Seoudi",
    "seoudi supermarket": "Seoudi",
    "سعودي": "Seoudi",
    # Abu Zekry group
    "Abu Zekry": "Abu Zekry",
    "Abu Zikri": "Abu Zekry",
    "abou zekry": "Abu Zekry",
    "أبو زكري": "Abu Zekry",
    # Khair Zaman group
    "Khair Zaman": "Khair Zaman",
    "Kheir Zaman": "Khair Zaman",
    "kheir zaman": "Khair Zaman",
    "خير زمان": "Khair Zaman",
}

for raw in shops["raw_name"].unique():
    if raw in chain_rules:
        raw_to_normalised[raw] = chain_rules[raw]
    else:
        # Local Shop entries keep their raw name
        raw_to_normalised[raw] = raw

# Apply to create a normalised_name column
shops["normalised_name"] = shops["raw_name"].map(raw_to_normalised)

# Verify: every shop_id maps to exactly one normalised_name
dup_check = shops.groupby("shop_id")["normalised_name"].nunique()
assert (dup_check == 1).all(), "Some shop_ids have multiple normalised_names!"

# ---------------------------------------------------------------------------
# 3. Tidy anchor names
# ---------------------------------------------------------------------------
# Strip leading/trailing whitespace, collapse multiple spaces, title-case
def tidy_anchor_name(name):
    import re
    name = name.strip()
    name = re.sub(r'\s+', ' ', name)
    # Title case
    name = name.title()
    return name

anchors["anchor_name_normalised"] = anchors["anchor_name"].apply(tidy_anchor_name)

# ---------------------------------------------------------------------------
# 4. KNN: for each anchor, find the 5 nearest shops
# ---------------------------------------------------------------------------
# Extract coordinates as numpy arrays for fast distance computation
shop_coords = np.array([(p.x, p.y) for p in shops.geometry])
anchor_coords = np.array([(p.x, p.y) for p in anchors.geometry])

shop_ids = shops["shop_id"].values
shop_names = shops["normalised_name"].values

n_anchors = len(anchors)
n_shops = len(shops)
K = 5

# We'll compute Euclidean distances (metric CRS -> metres)
knn_results = []  # list of list of dicts per anchor

for a_idx in range(n_anchors):
    ax, ay = anchor_coords[a_idx]
    dx = shop_coords[:, 0] - ax
    dy = shop_coords[:, 1] - ay
    dists = np.sqrt(dx * dx + dy * dy)
    
    # Get indices of the 5 nearest shops
    nearest_indices = np.argsort(dists)[:K]
    
    entries = []
    for idx in nearest_indices:
        d = float(dists[idx])
        entries.append({
            "shop_id": str(shop_ids[idx]),
            "normalised_name": str(shop_names[idx]),
            "distance_m": round(d, 2),
            "within_1km": d <= 1000.0
        })
    knn_results.append(entries)

# ---------------------------------------------------------------------------
# 5. Distance matrix: for each anchor, compute distances from its 5 knn shops
#    to its 3 closest sibling anchors
# ---------------------------------------------------------------------------
# First compute all anchor-to-anchor distances
anchor_dist_matrix = np.zeros((n_anchors, n_anchors))
for i in range(n_anchors):
    ax, ay = anchor_coords[i]
    dx = anchor_coords[:, 0] - ax
    dy = anchor_coords[:, 1] - ay
    anchor_dist_matrix[i, :] = np.sqrt(dx * dx + dy * dy)

# For each anchor, find the 3 closest OTHER anchors (siblings)
# Exclude self (distance 0)
sibling_indices_per_anchor = []
for a_idx in range(n_anchors):
    dists = anchor_dist_matrix[a_idx, :].copy()
    dists[a_idx] = np.inf  # exclude self
    closest_3 = np.argsort(dists)[:3]
    sibling_indices_per_anchor.append(closest_3)

# For each anchor, build the 5x3 distance matrix
# Rows follow knn order, columns correspond to sibling anchors in distance-ascending order
full_distance_matrices = []

for a_idx in range(n_anchors):
    # Get the 5 nearest shop indices for this anchor
    ax, ay = anchor_coords[a_idx]
    dx = shop_coords[:, 0] - ax
    dy = shop_coords[:, 1] - ay
    dists = np.sqrt(dx * dx + dy * dy)
    knn_indices = np.argsort(dists)[:K]
    
    # Get the 3 closest sibling anchor indices
    sib_indices = sibling_indices_per_anchor[a_idx]
    
    # Build 5x3 matrix: for each knn shop, compute distance to each sibling anchor
    matrix = np.zeros((K, 3))
    for row, shop_idx in enumerate(knn_indices):
        sx, sy = shop_coords[shop_idx]
        for col, sib_idx in enumerate(sib_indices):
            sx2, sy2 = anchor_coords[sib_idx]
            d = np.sqrt((sx - sx2)**2 + (sy - sy2)**2)
            matrix[row, col] = round(float(d), 2)
    
    full_distance_matrices.append(matrix.tolist())

# ---------------------------------------------------------------------------
# 6. Assemble output JSON
# ---------------------------------------------------------------------------
output = []
for a_idx in range(n_anchors):
    record = {
        "anchor_id": str(anchors.iloc[a_idx]["anchor_id"]),
        "anchor_name_normalised": str(anchors.iloc[a_idx]["anchor_name_normalised"]),
        "knn": knn_results[a_idx],
        "full_distance_matrix_m": full_distance_matrices[a_idx]
    }
    output.append(record)

with open("/work/market_neighbourhoods.json", "w", encoding="utf-8") as f:
    json.dump(output, f, ensure_ascii=False, indent=2)

print(f"Done. Wrote {len(output)} anchor records to market_neighbourhoods.json")

# ---------------------------------------------------------------------------
# 7. Sanity checks
# ---------------------------------------------------------------------------
# Check a few records
print("\n--- Sample record (first anchor) ---")
print(json.dumps(output[0], ensure_ascii=False, indent=2))

# Verify all knn have exactly 5 entries
for rec in output:
    assert len(rec["knn"]) == 5, f"Anchor {rec['anchor_id']} has {len(rec['knn'])} knn entries, expected 5"
    for e in rec["knn"]:
        assert isinstance(e["distance_m"], (int, float)) and np.isfinite(e["distance_m"]), \
            f"Non-finite distance in {rec['anchor_id']}"
        assert isinstance(e["within_1km"], bool), \
            f"within_1km not bool in {rec['anchor_id']}"
    # Check matrix shape
    assert len(rec["full_distance_matrix_m"]) == 5, f"Matrix wrong rows for {rec['anchor_id']}"
    for row in rec["full_distance_matrix_m"]:
        assert len(row) == 3, f"Matrix wrong cols for {rec['anchor_id']}"

print("\nAll sanity checks passed!")
Wrote /work/solve.py (8375 chars).
1:49
1:49
Bash cd /work && python solve.py
Done. Wrote 100 anchor records to market_neighbourhoods.json

--- Sample record (first anchor) ---
{
  "anchor_id": "M001",
  "anchor_name_normalised": "Tahrir Square Plaza",
  "knn": [
    {
      "shop_id": "S07233",
      "normalised_name": "Seoudi",
      "distance_m": 565.58,
      "within_1km": true
    },
    {
      "shop_id": "S05762",
      "normalised_name": "Oscar",
      "distance_m": 722.33,
      "within_1km": true
    },
    {
      "shop_id": "S08811",
      "normalised_name": "Seoudi",
      "distance_m": 851.15,
      "within_1km": true
    },
    {
      "shop_id": "S01722",
      "normalised_name": "Local Shop 01722",
      "distance_m": 852.44,
      "within_1km": true
    },
    {
      "shop_id": "S04111",
      "normalised_name": "Local Shop 04111",
      "distance_m": 868.96,
      "within_1km": true
    }
  ],
  "full_distance_matrix_m": [
    [
      2487.01,
      2174.32,
      3254.51
    ],
    [
      1534.48,
      1756.65,
      2217.73
    ],
    [
      1083.48,
      2432.54,
      2523.22
    ],
    [
      1080.92,
      2423.44,
      2512.91
    ],
    [
      2589.65,
      1669.14,
      2955.68
    ]
  ]
}

All sanity checks passed!
1:58
The script ran successfully. Let me verify the output file and do some final sanity checks.

Let me check:
1. The file exists
2. The structure is as expected
3. The anchor names are properly normalised (stripped of whitespace, title-cased)
4. Let me check a few more sample records to be sure
1:58
1:58
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
with open('market_neighbourhoods.json') as f:
    data = json.load(f)

print(f'Total records: {len(data)}')
print()

# Check first 3 anchor names
for rec in data[:3]:
    print(f'{rec[\"anchor_id\"]}: {rec[\"anchor_name_normalised\"]!r}')
print()

# Check a few more scattered
for rec in data[10:20]:
    print(f'{rec[\"anchor_id\"]}: {rec[\"anchor_name_normalised\"]!r}')
print()

# Check some that had leading/trailing whitespace originally
# e.g. '  Abdeen Palace Edge  ' -> should be 'Abdeen Palace Edge'
targets = ['Abdeen Palace Edge', 'Bab Al Louq Corner', 'Boulaq Edge']
for rec in data:
    if rec['anchor_name_normalised'] in targets:
        print(f'Found tidy: {rec[\"anchor_id\"]}: {rec[\"anchor_name_normalised\"]!r}')
"
Total records: 100

M001: 'Tahrir Square Plaza'
M002: 'Zamalek Riverside'
M003: 'Maadi Corniche'

M011: 'Roxy Square'
M012: 'Ramses Crossing'
M013: 'Sayeda Zeinab Plaza'
M014: 'Khan El Khalili Approach'
M015: 'Bab Al Louq Corner'
M016: 'Korba Quarter'
M017: 'Manial Riverbank'
M018: 'Shubra North'
M019: 'Ain Shams Plaza'
M020: 'Abbasiya Junction'

Found tidy: M015: 'Bab Al Louq Corner'
Found tidy: M021: 'Boulaq Edge'
Found tidy: M057: 'Abdeen Palace Edge'
2:07
Let me check a few more things:

1. The anchor names - let me check the original anchor names that had whitespace issues, like "  Abdeen Palace Edge  " - it seems to have been properly tidied to "Abdeen Palace Edge". Good.

2. Let me check that all anchor_name_normalised are non-empty strings.

3. Let me also verify the `within_1km` flag logic more carefully - it should be true when distance_m <= 1000.

4. Let me also check that the `normalised_name` for shops is consistent per shop_id.

Let me also check a few more details - the anchor names like "MAADI CORNISH" should be "Maadi Cornish" but there's also "Maadi Corniche" from another anchor. Let me check these.
2:07
2:07
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
with open('market_neighbourhoods.json') as f:
    data = json.load(f)

# Check all anchor names are non-empty
for rec in data:
    name = rec['anchor_name_normalised']
    assert isinstance(name, str) and len(name) > 0, f'Empty name for {rec[\"anchor_id\"]}'

# Show all anchor names
for rec in data:
    print(f'{rec[\"anchor_id\"]}: {rec[\"anchor_name_normalised\"]!r}')
"
M001: 'Tahrir Square Plaza'
M002: 'Zamalek Riverside'
M003: 'Maadi Corniche'
M004: 'Heliopolis Square'
M005: 'New Cairo Tagamoa'
M006: 'Nasr City Hub'
M007: 'Mohandessin Centre'
M008: 'Garden City Walk'
M009: 'Dokki Market'
M010: 'Giza Pyramids Gate'
M011: 'Roxy Square'
M012: 'Ramses Crossing'
M013: 'Sayeda Zeinab Plaza'
M014: 'Khan El Khalili Approach'
M015: 'Bab Al Louq Corner'
M016: 'Korba Quarter'
M017: 'Manial Riverbank'
M018: 'Shubra North'
M019: 'Ain Shams Plaza'
M020: 'Abbasiya Junction'
M021: 'Boulaq Edge'
M022: 'Garbiya Plaza'
M023: 'Sakakini Approach'
M024: 'Dar El Salaam'
M025: 'El Marg Hub'
M026: 'Helwan Centre'
M027: 'Maasara Crossing'
M028: 'Tora Edge'
M029: 'Mokattam Heights'
M030: 'Nozha Promenade'
M031: 'Sheraton Heliopolis'
M032: 'Triumph Square'
M033: 'Cleopatra Plaza'
M034: 'Salah Salem Strip'
M035: 'Autostrad Corner'
M036: 'El Rehab Gate One'
M037: 'El Rehab Gate Two'
M038: 'Madinaty Promenade'
M039: 'Fifth Settlement North'
M040: 'Fifth Settlement South'
M041: 'American University Gate'
M042: 'Police Academy Strip'
M043: 'Ring Road North'
M044: 'Ring Road East'
M045: 'Ring Road West'
M046: 'City Stars Mall'
M047: 'Cairo Festival City'
M048: 'Mall Of Egypt Gate'
M049: 'Tagamoa First'
M050: 'Tagamoa Third'
M051: 'El Mokattam Plateau'
M052: 'Al Ahly Stadium'
M053: 'Cairo Stadium'
M054: 'Sharkawi Plaza'
M055: 'El Obour Hub'
M056: 'Shoubra Mazallat'
M057: 'Abdeen Palace Edge'
M058: 'El Hussein Square'
M059: 'Al Ghouriya Strip'
M060: 'El Mosky Quarter'
M061: 'Bab Zuweila Approach'
M062: 'Ataba Square'
M063: 'Opera Square'
M064: 'Talaat Harb Plaza'
M065: 'Soliman Pasha Corner'
M066: 'Sherif Street'
M067: 'Qasr El Nile'
M068: 'Kasr El Aini Strip'
M069: 'El Sayeda Aisha'
M070: 'Kobri El Qubba'
M071: 'Mar Mina Plaza'
M072: 'Saint Fatima Hub'
M073: 'El Nozha El Gedida'
M074: 'Rabaa Square'
M075: 'Tagamoa El Saba'
M076: 'Bahteem Crossing'
M077: 'El Salam City'
M078: 'Madinet Nasr Eighth Zone'
M079: 'Madinet Nasr Tenth Zone'
M080: 'El Hadaba El Wosta'
M081: 'Mokattam Sector One'
M082: 'Mokattam Sector Six'
M083: 'El Maadi Degla'
M084: 'Maadi Sarayat'
M085: 'Maadi Cornish'
M086: 'Old Cairo Babylon'
M087: 'Coptic Cairo Plaza'
M088: 'Fustat Park Edge'
M089: 'Manial Bridge'
M090: 'Embaba Crossing'
M091: 'Imbaba Airport Strip'
M092: 'Mit Okba Plaza'
M093: 'El Agouza Riverside'
M094: 'El Sahel Junction'
M095: 'Rod El Farag Bridge'
M096: 'Shubra El Kheima Centre'
M097: 'El Sawah Corner'
M098: 'Demerdash Plaza'
M099: 'El Demerdash Hospital Edge'
M100: 'Ramses Hilton Plaza'
2:24
The anchor names look nicely tidied. Let me also verify that the original "MAADI CORNISH" is correctly handled - it became "Maadi Cornish" which might be a misspelling of "Corniche" but it's just title-casing the original data, so that's fine.

Let me now do a more thorough check: verify the `within_1km` flag logic, verify distances are in metres (metric CRS), and check a spot-check of the distance matrix.

Also let me verify that the normalised names are consistent across all shops - the same shop_id always has the same normalised_name, and shops from the same chain share a spelling.
2:24
2:24
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
import numpy as np

# Verify within_1km logic
with open('market_neighbourhoods.json') as f:
    data = json.load(f)

errors = []
for rec in data:
    for e in rec['knn']:
        expected = e['distance_m'] <= 1000.0
        if e['within_1km'] != expected:
            errors.append(f'{rec[\"anchor_id\"]} shop {e[\"shop_id\"]} dist={e[\"distance_m\"]} flag={e[\"within_1km\"]}')
    # Verify all within_1km are bool
    for e in rec['knn']:
        if not isinstance(e['within_1km'], bool):
            errors.append(f'within_1km not bool for {rec[\"anchor_id\"]} {e[\"shop_id\"]}')

if errors:
    print('ERRORS:', errors[:10])
else:
    print('All within_1km flags correct')

# Verify matrix is 5x3 numeric
for rec in data:
    m = rec['full_distance_matrix_m']
    if len(m) != 5:
        errors.append(f'Matrix rows != 5 for {rec[\"anchor_id\"]}')
    for r in m:
        if len(r) != 3:
            errors.append(f'Matrix cols != 3 for {rec[\"anchor_id\"]}')
        for v in r:
            if not isinstance(v, (int, float)) or not np.isfinite(v):
                errors.append(f'Non-finite matrix value for {rec[\"anchor_id\"]}')

if not errors:
    print('All matrices are 5x3 with finite numeric values')

# Check knn sorted ascending
for rec in data:
    dists = [e['distance_m'] for e in rec['knn']]
    for i in range(len(dists)-1):
        if dists[i] > dists[i+1] + 1e-6:
            errors.append(f'KNN not sorted for {rec[\"anchor_id\"]}')
            break

if not errors:
    print('All knn entries are sorted by ascending distance_m')
    
# Check that distance_m values seem reasonable (metric CRS, Cairo area)
import geopandas as gpd
shops = gpd.read_file('/work/cairo_retail.gpkg', layer='shops')
print(f'Shop coords range: x [{shops.geometry.x.min():.0f}, {shops.geometry.x.max():.0f}], y [{shops.geometry.y.min():.0f}, {shops.geometry.y.max():.0f}]')

# Spot check: verify distances computed correctly for first anchor
anchors = gpd.read_file('/work/cairo_retail.gpkg', layer='anchors')
a0 = anchors.iloc[0]
s0 = shops[shops['shop_id'] == 'S07233'].iloc[0]
import math
d = math.sqrt((s0.geometry.x - a0.geometry.x)**2 + (s0.geometry.y - a0.geometry.y)**2)
print(f'Spot check M001 -> S07233: computed={d:.2f}, reported={data[0][\"knn\"][0][\"distance_m\"]}')
"
All within_1km flags correct
All matrices are 5x3 with finite numeric values
All knn entries are sorted by ascending distance_m
Shop coords range: x [634125, 653413], y [804502, 826665]
Spot check M001 -> S07233: computed=565.58, reported=565.58
2:37
Let me also check the "Maadi Cornish" inconsistency. Original anchor name was "MAADI CORNISH" but there was also "Maadi Corniche" (with a space after it). Let me check the original data more carefully.

Actually looking at the original anchor names:
- `'  Maadi Corniche  '` → "Maadi Corniche" 
- `'MAADI CORNISH'` → "Maadi Cornish"

These are two different anchors with different spelling. That's fine since they're separate anchors. Let me also verify the file exists and has appropriate content.

Let me do one more check: verify the `within_1km` is truly boolean (not integer 0/1) in the JSON file.
2:37
2:37
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
with open('market_neighbourhoods.json') as f:
    data = json.load(f)

# Check types of within_1km
for rec in data:
    for e in rec['knn']:
        t = type(e['within_1km'])
        if t != bool:
            print(f'TYPE ERROR: {rec[\"anchor_id\"]} {e[\"shop_id\"]} within_1km is {t}')
        t2 = type(e['distance_m'])
        if t2 not in (int, float):
            print(f'TYPE ERROR: {rec[\"anchor_id\"]} {e[\"shop_id\"]} distance_m is {t2}')

# Also ensure normalised_name is non-empty
for rec in data:
    if not rec['anchor_name_normalised']:
        print(f'EMPTY anchor name for {rec[\"anchor_id\"]}')

# Verify chain names consistent
shop_name_map = {}
for rec in data:
    for e in rec['knn']:
        sid = e['shop_id']
        nname = e['normalised_name']
        if sid in shop_name_map:
            if shop_name_map[sid] != nname:
                print(f'INCONSISTENT: {sid} had {shop_name_map[sid]} now {nname}')
        else:
            shop_name_map[sid] = nname
print(f'Checked {len(shop_name_map)} unique shop_ids across all knn entries')
print('All good!')
"
Checked 480 unique shop_ids across all knn entries
All good!
2:51
2:51
Bash ls -la /work/market_neighbourhoods.json
-rw-r--r-- 1 agent agent 120426 Jun 16 07:51 /work/market_neighbourhoods.json
2:56
2:56
Bash cd /work && python3 -c "
cd /work && python3 -c "
import json
with open('market_neighbourhoods.json') as f:
    data = json.load(f)
assert isinstance(data, list), 'Not a top-level array'
print(f'Top-level JSON array with {len(data)} records')
print('Keys in first record:', sorted(data[0].keys()))
print('Keys in knn entry:', sorted(data[0]['knn'][0].keys()))
print('Matrix shape:', len(data[0]['full_distance_matrix_m']), 'x', len(data[0]['full_distance_matrix_m'][0]))
"
Top-level JSON array with 100 records
Keys in first record: ['anchor_id', 'anchor_name_normalised', 'full_distance_matrix_m', 'knn']
Keys in knn entry: ['distance_m', 'normalised_name', 'shop_id', 'within_1km']
Matrix shape: 5 x 3
3:06
Everything looks good. Let me summarise what was done:

1. Loaded `cairo_retail.gpkg` with 10,000 shops and 100 anchors, both in EPSG:22992 (metric CRS)
2. Built a name normalisation mapping for shops:
   - "OSCAR", "Oscar", "Oscar Grand Stores", "اوسكار" → "Oscar"
   - "Carrefour", "Carrefour Egypt", "carrefour", "كارفور" → "Carrefour"
   - "Hyper One", "HyperOne", "hyperone", "هايبر وان" → "Hyper One"
   - "Metro Market", "Metro Markets", "metro", "مترو" → "Metro"
   - "Spinneys", "Spineys", "spinneys cairo", "سبينيز" → "Spinneys"
   - "Seoudi", "Seoudi Market", "seoudi supermarket", "سعودي" → "Seoudi"
   - "Abu Zekry", "Abu Zikri", "abou zekry", "أبو زكري" → "Abu Zekry"
   - "Khair Zaman", "Kheir Zaman", "kheir zaman", "خير زمان" → "Khair Zaman"
   - "Local Shop XXXXX" entries kept as-is
3. Tidied anchor names by stripping whitespace, collapsing multiple spaces, and title-casing
4. For each anchor: found the 5 nearest shops by Euclidean distance (metric CRS → metres), computed within_1km flag
5. For each anchor: computed the 3 closest sibling anchors, then built a 5x3 distance matrix from the 5 knn shops to those 3 siblings
6. Wrote `market_neighbourhoods.json` as a top-level JSON array with 100 records

All sanity checks pass:
- All 100 anchor records present
- Each has exactly 5 knn entries sorted by ascending distance
- All distances are finite numeric values in metres
- All within_1km flags are boolean and correctly computed
- All matrices are 5x3 with finite numeric values
- Shop names are consistent (same shop_id always same normalised_name)
- Anchor names are non-empty strings
- Spot-checked a distance computation manually - matches
3:06