5454 "repo_root" ,
5555 "resolve_repo_data_dir" ,
5656 "repo_names" ,
57+ "registry_spec" ,
58+ "registry_column_map" ,
59+ "registry_crs" ,
5760 "registry_df" ,
5861 "repo_registry" ,
5962 "source_priority_group" ,
@@ -412,6 +415,23 @@ def repo_registry(repo=None, repo_cfg=None):
412415 if isinstance (registry_names , str ):
413416 registry_names = [registry_names ]
414417
418+ # Validate CRS consistency across sub-registries
419+ if len (registry_names ) > 1 :
420+ crs_values = []
421+ for rname in registry_names :
422+ crs = registry_crs (rname )
423+ if crs is not None :
424+ crs_values .append ((rname , crs ))
425+ if len (crs_values ) > 1 :
426+ first_name , first_crs = crs_values [0 ]
427+ for other_name , other_crs in crs_values [1 :]:
428+ if other_crs != first_crs :
429+ raise ValueError (
430+ f"CRS mismatch across registries: "
431+ f"{ first_name } declares { first_crs } but "
432+ f"{ other_name } declares { other_crs } "
433+ )
434+
415435 dfs = []
416436 for rname in registry_names :
417437 sub = registry_df (rname ).copy ()
@@ -424,19 +444,10 @@ def repo_registry(repo=None, repo_cfg=None):
424444 )
425445 dfs .append (sub )
426446
427- # Merge on common columns
447+ # Merge registries (union of columns, no key overlap allowed)
428448 if len (dfs ) == 1 :
429449 db = dfs [0 ]
430450 else :
431- common_cols = set (dfs [0 ].columns )
432- for sub in dfs [1 :]:
433- common_cols &= set (sub .columns )
434- common_cols = sorted (common_cols )
435- if site_key not in common_cols :
436- raise ValueError (
437- f"Column { site_key !r} not in common columns across registries"
438- )
439-
440451 # Check for key overlap across sub-registries
441452 all_keys = []
442453 for i , sub in enumerate (dfs ):
@@ -448,7 +459,6 @@ def repo_registry(repo=None, repo_cfg=None):
448459 )
449460 all_keys .extend (sub [site_key ].values )
450461
451- dfs = [sub [common_cols ] for sub in dfs ]
452462 db = pd .concat (dfs , ignore_index = True )
453463
454464 dup = db [site_key ].duplicated ()
@@ -547,6 +557,39 @@ def repo_config(repo_name):
547557 _repo_cache [repo_name ] = spec
548558 return spec
549559
560+ def registry_spec (registry_name ):
561+ """
562+ Return the raw registry specification dict for a named registry.
563+
564+ If the registry is declared as a bare string (legacy), returns
565+ ``{"file": <string>}``.
566+ """
567+ registries = config .get ("registries" , {})
568+ if registry_name not in registries :
569+ raise ValueError (f"Registry not found: { registry_name } " )
570+ spec = registries [registry_name ]
571+ if isinstance (spec , str ):
572+ return {"file" : spec }
573+ return dict (spec )
574+
575+
576+ def registry_column_map (registry_name ):
577+ """
578+ Return the column_map for a named registry, or empty dict if unset.
579+ """
580+ spec = registry_spec (registry_name )
581+ return dict (spec .get ("column_map" , {}) or {})
582+
583+
584+ def registry_crs (registry_name ):
585+ """
586+ Return the crs dict for a named registry, or None if unset.
587+ """
588+ spec = registry_spec (registry_name )
589+ crs = spec .get ("crs" , None )
590+ return dict (crs ) if crs else None
591+
592+
550593def registry_df (registry_name ):
551594 """
552595 Load a raw registry table by name.
@@ -604,7 +647,9 @@ def registry_df(registry_name):
604647 if registry_name not in registries :
605648 raise ValueError (f"Registry not found: { registry_name } " )
606649
607- reg_path = _resolve_config_path (registries [registry_name ])
650+ spec = registries [registry_name ]
651+ fname = spec ["file" ] if isinstance (spec , dict ) else spec
652+ reg_path = _resolve_config_path (fname )
608653 if not os .path .exists (reg_path ):
609654 raise ValueError (f"Registry file not found: { reg_path } " )
610655
0 commit comments