1818from yamlcore import CoreLoader # type: ignore
1919
2020from overture .schema .core import OvertureFeature
21- from overture .schema .system .discovery import ModelKey , discover_models , tags_by_key
21+ from overture .schema .system .discovery import (
22+ ModelKey ,
23+ discover_models ,
24+ filter_models ,
25+ tags_by_key ,
26+ )
2227from overture .schema .system .feature import Feature
2328from overture .schema .system .json_schema import json_schema
2429
@@ -189,72 +194,32 @@ def validate_features(data: list, model_type: UnionType) -> list[BaseModel]:
189194
190195
191196def resolve_types (
192- use_overture_types : bool ,
193- namespace : str | None ,
194- theme_names : tuple [str , ...],
197+ tags : tuple [str , ...],
198+ excluded_tags : tuple [str , ...],
195199 type_names : tuple [str , ...],
196200) -> UnionType :
197201 """Resolve CLI options into a model type suitable for parse_feature.
198202
199203 Args
200204 ----
201- use_overture_types: Boolean from --overture-types flag
202- namespace: Namespace to filter by (e.g., "overture", "annex")
203- theme_names: List of theme names from --theme option
205+ tags: Tags to include (e.g., "feature", "overture:theme=buildings")
206+ excluded_tags: Tags to exclude (e.g., "draft")
204207 type_names: List of type names from --type option
205208
206209 Returns
207210 -------
208211 Model type suitable for passing to parse_feature
209212 """
210- # Discover models once with the appropriate namespace
211- all_models = discover_models ()
213+ # Discover models
214+ models : ModelDict = discover_models ()
212215
213216 # Filter models based on CLI options
214- filtered_models : ModelDict = {}
215-
216- if namespace and namespace != "overture" :
217- filtered_models = {
218- key : model_class
219- for key , model_class in all_models .items ()
220- if namespace in key .tags
221- }
222-
223- if use_overture_types :
224- for key , model_class in all_models .items ():
225- if tags_by_key (key .tags , "overture:theme" ):
226- filtered_models [key ] = model_class
227-
228- elif theme_names and not type_names :
229- # Theme-only mode: all types in specified themes
230- for key , model_class in all_models .items ():
231- if next (iter (tags_by_key (key .tags , "overture:theme" )), None ) in theme_names :
232- filtered_models [key ] = model_class
233-
234- elif type_names and not theme_names :
235- # Type-only mode: find matching types across all themes
236- for key , model_class in all_models .items ():
237- if key .name in type_names and tags_by_key (key .tags , "overture:theme" ):
238- filtered_models [key ] = model_class
239-
240- elif type_names and theme_names :
241- # Both specified: find matching types within specified themes
242- for key , model_class in all_models .items ():
243- if (
244- key .name in type_names
245- and next (iter (tags_by_key (key .tags , "overture:theme" )), None )
246- in theme_names
247- ):
248- filtered_models [key ] = model_class
249-
250- else :
251- # No filters specified - use all models
252- filtered_models = all_models
217+ models = filter_models (models , tags , excluded_tags , type_names )
253218
254- if not filtered_models :
219+ if not models :
255220 raise ValueError ("No models found matching the specified criteria" )
256221
257- return create_union_type_from_models (filtered_models )
222+ return create_union_type_from_models (models )
258223
259224
260225def get_source_name (filename : Path ) -> str :
@@ -290,10 +255,10 @@ def cli() -> None:
290255 $ overture-schema list-types
291256 \b
292257 # Generate JSON schema
293- $ overture-schema json-schema --theme buildings
258+ $ overture-schema json-schema --tag overture:theme= buildings
294259 \b
295260 # Validate specific types
296- $ overture-schema validate --theme buildings data.json
261+ $ overture-schema validate --tag overture:theme= buildings data.json
297262 """
298263 pass
299264
@@ -536,7 +501,7 @@ def handle_validation_error(
536501 style = "yellow" ,
537502 )
538503 stderr .print (
539- " • Consider validating each type separately with --theme or --type" ,
504+ " • Consider validating each type separately with --tag or --type" ,
540505 style = "dim" ,
541506 )
542507 stderr .print ()
@@ -557,7 +522,7 @@ def handle_validation_error(
557522 style = "yellow" ,
558523 )
559524 stderr .print (
560- " • Specifying --theme or --type to narrow validation" , style = "dim"
525+ " • Specifying --tag or --type to narrow validation" , style = "dim"
561526 )
562527 stderr .print (" • Adding discriminator fields to clarify intent" , style = "dim" )
563528 stderr .print ()
@@ -637,18 +602,16 @@ def handle_generic_error(e: Exception, filename: Path, error_type: str) -> None:
637602@cli .command ()
638603@click .argument ("filename" , type = click .Path (path_type = Path ), required = True )
639604@click .option (
640- "--overture-types" ,
641- is_flag = True ,
642- help = "Validate against all official Overture types (excludes extensions)" ,
643- )
644- @click .option (
645- "--namespace" ,
646- help = "Namespace to filter by (e.g., overture, annex)" ,
605+ "--tag" ,
606+ "tags" ,
607+ multiple = True ,
608+ help = "Tags to include (e.g., overture:theme=addresses)" ,
647609)
648610@click .option (
649- "--theme" ,
611+ "--exclude-tag" ,
612+ "excluded_tags" ,
650613 multiple = True ,
651- help = "Theme to validate against (shorthand for all types in theme)" ,
614+ help = "Tags to exclude (e.g., overture: theme=base )" ,
652615)
653616@click .option (
654617 "--type" ,
@@ -664,9 +627,8 @@ def handle_generic_error(e: Exception, filename: Path, error_type: str) -> None:
664627)
665628def validate (
666629 filename : Path ,
667- overture_types : bool ,
668- namespace : str | None ,
669- theme : tuple [str , ...],
630+ tags : tuple [str , ...],
631+ excluded_tags : tuple [str , ...],
670632 types : tuple [str , ...],
671633 show_fields : tuple [str , ...],
672634) -> None :
@@ -684,17 +646,17 @@ def validate(
684646 $ overture-schema validate - < data.json
685647 \b
686648 # Validate only buildings
687- $ overture-schema validate --theme buildings data.json
649+ $ overture-schema validate --tag overture:theme= buildings data.json
688650 \b
689651 # Validate specific type
690652 $ overture-schema validate --type building data.json
691653 \b
692654 # Official Overture types only
693- $ overture-schema validate --overture-types data.json
655+ $ overture-schema validate --tag overture --tag feature data.json
694656 """
695657 # Resolve model type first (errors here are ValueErrors, not ValidationErrors)
696658 try :
697- model_type = resolve_types (overture_types , namespace , theme , types )
659+ model_type = resolve_types (tags , excluded_tags , types )
698660 except ValueError as e :
699661 handle_generic_error (e , filename , "value" )
700662 return
@@ -722,18 +684,16 @@ def validate(
722684
723685@cli .command ("json-schema" )
724686@click .option (
725- "--overture-types" ,
726- is_flag = True ,
727- help = "Generate schema for all official Overture types (excludes extensions)" ,
728- )
729- @click .option (
730- "--namespace" ,
731- help = "Namespace to filter by (e.g., overture, annex)" ,
687+ "--tag" ,
688+ "tags" ,
689+ multiple = True ,
690+ help = "Tags to include (e.g., overture:theme=addresses)" ,
732691)
733692@click .option (
734- "--theme" ,
693+ "--exclude-tag" ,
694+ "excluded_tags" ,
735695 multiple = True ,
736- help = "Theme to generate schema for (shorthand for all types in theme)" ,
696+ help = "Tags to exclude (e.g., overture: theme=base )" ,
737697)
738698@click .option (
739699 "--type" ,
@@ -742,9 +702,8 @@ def validate(
742702 help = "Specific type to generate schema for (e.g., building, segment)" ,
743703)
744704def json_schema_command (
745- overture_types : bool ,
746- namespace : str | None ,
747- theme : tuple [str , ...],
705+ tags : tuple [str , ...],
706+ excluded_tags : tuple [str , ...],
748707 types : tuple [str , ...],
749708) -> None :
750709 r"""Generate JSON schema for Overture Maps types.
@@ -757,17 +716,17 @@ def json_schema_command(
757716 # All types
758717 $ overture-schema json-schema > schema.json
759718 \b
760- # Buildings theme
761- $ overture-schema json-schema --theme buildings
719+ # Buildings theme by tag
720+ $ overture-schema json-schema --tag overture:theme= buildings
762721 \b
763722 # Specific types
764723 $ overture-schema json-schema --type building
765724 \b
766725 # Official Overture types only
767- $ overture-schema json-schema --overture-types
726+ $ overture-schema json-schema --tag overture --tag feature
768727 """
769728 try :
770- model_type = resolve_types (overture_types , namespace , theme , types )
729+ model_type = resolve_types (tags , excluded_tags , types )
771730 schema = json_schema (model_type )
772731 # Use plain print for JSON output to avoid Rich formatting
773732 print (json .dumps (schema , indent = 2 , sort_keys = True ))
@@ -786,7 +745,7 @@ def json_schema_command(
786745 "--exclude-tag" ,
787746 "excluded_tags" ,
788747 multiple = True ,
789- help = "Filter types by tag (e.g., overture:theme=base)" ,
748+ help = "Exclude types by tag (e.g., overture:theme=base)" ,
790749)
791750@click .option (
792751 "--group-by" ,
@@ -797,8 +756,7 @@ def list_types(
797756) -> None :
798757 r"""List all available types grouped by theme with descriptions.
799758
800- Displays all registered Overture Maps types organized by theme,
801- including model class names and docstrings.
759+ Displays all registered Overture Maps types and can organized by grouping.
802760
803761 \b
804762 Examples:
@@ -807,21 +765,8 @@ def list_types(
807765 """
808766 try :
809767 models = discover_models ()
810- filters = []
811-
812- if tags :
813- filters .append (lambda key : all (tag in key .tags for tag in tags ))
814- if excluded_tags :
815- filters .append (
816- lambda key : not any (tag in key .tags for tag in excluded_tags )
817- )
818768
819- if filters :
820- models = {
821- key : model
822- for key , model in models .items ()
823- if all (f (key ) for f in filters )
824- }
769+ models = filter_models (models , tags = tags , excluded_tags = excluded_tags )
825770
826771 if group_by :
827772 grouped_models : dict [str , set [ModelKey ]] = {}
0 commit comments