Metacity
  • Welcome
  • Metacity
    • Installation
    • [dev] CGAL Dependency
    • Data Import
    • Models and Attributes
    • Layers
    • Grids
    • Quad-trees
    • Dockerized Processor
    • Development notes
  • Archives
    • DataAPI
      • Development
    • MetacityGL
      • Installation
      • Creating a MetacityGL App
      • Writing Custom Layers
      • Loading Metacity Data
      • Development notes
    • BananaGL
      • Development
    • AMOS
      • Synthetic Population
        • Case Studies
        • Data Specification
        • Activity Chains
        • Spatial Distribution
        • MATSim population input and comparison
        • Households
      • MATSim
        • Basic Info
        • Running Scenario
          • Config File
          • Current state
        • Input information
          • OSM
          • Filtering GTFS
          • Coordinates
        • Output
          • Processing
      • OICT
        • MapResolver
      • Resources
        • Trafic Simulation
        • MATSim
        • Vis and Meta Software
        • Synthetic Population
        • Public Relations
        • Inspiration
    • Metacity Block Edition
      • Epic Games Mega Grant
      • 🧠Internal notes
  • Links
    • Homepage
    • GitHub
  • Code of Conduct
Powered by GitBook
On this page
  • Attributes
  • Geometry Type
  • Attribute Caveats
Edit on GitHub
  1. Metacity

Models and Attributes

Using Metacity's models and geometry attributes

PreviousData ImportNextLayers

Last updated 2 years ago

A Model is a universal entity for storing geometry and metadata, it has no specific semantic meaning.

  • The geometry is stored in Attributes. Models can have multiple Attributes - see section .

  • The properties can be attached to a model as a metadata

See the following example:

from metacity.geometry import Model, Attribute

model = Model()
position_attr = Attribute()
position_attr.push_polygon3D([[0, 0, 0, \
                               0, 0, 1, \
                               0, 1, 1]])
                          
#Model.add_attribute(self, attr_name: str, attr: Attribute) -> None
model.add_attribute("POSITION", position_attr)

#Model.set_metadata(self, metadata: dict) -> None
model.set_metadata({ "description": "An example triangle" })

Additionally, the Model class has the following methods:

#Complementary to method add_attribute, there is also 
#Model.attribute_exists(self, attr_name: str) -> bool
assert model.attribute_exists("POSITION") == True

#Model.get_attribute(self, attr_name: str) -> Attribute
position_attr = model.get_attribute("POSITION")

Note that the property Model.metadata returns a copy. Updating it won't affect the metadata stored inside the model. If you wish to update the metadata, use the Model.set_metadata method:

#@property
#Model.metadata(self) -> dict
model.metadata["description"] = "A new description"
assert model.metadata["description"] != "A new description"

#updating the metadata
#Model.set_metadata(self, metadata: dict) -> None
model.set_metadata({ "description": "A new description" })
assert model.metadata["description"] == "A new description"

It is possible to check what is the geometry type of the Model. Note that models do not support mixing geometry types. If you need this feature, consider splitting your geometry into several Models.

#@property
#Model.geom_type(self) -> int
geometry_type_code = model.geom_type

Attributes

A unique name identifies each attribute. There are a few names that are commonly used for certain types of attributes:

Name
Description

POSITION

coordinates of the vertex positions

NORMAL

normal buffer

COLOR

vertex color

Metacity does not utilize indices for geometry reuse, all data is stored "as is" in sequential order.

If everything works as intended, a user should rarely work directly with Attributes. Occasionally, it might be useful to be able to access the Attribute API:

from metacity.geometry import Attribute

points = Attribute()
#Insert 2D points using push_line2D method:
#Attribute.push_point2D(self, points: List[float]) -> None
points.push_point2D([0, 0, \
                    1, 1, \
                    1, 2])
#Similar for 3D points
#Attribute.push_point3D(self, points: List[float]) -> None
points.push_point3D([0, 0, 0,   \
                     1, 1, 0.5, \
                     1, 2, 1])
                     
#@property
#Attribute.geom_type(self) -> int:
assert points.geom_type == 1

Parsing Lines is very similar to parsing points, although the main difference is the data gets stored as individual segments duplicating inner vertices.

from metacity.geometry import Attribute

lines = Attribute()
#Insert 2D line string using push_line2D method:
#Attribute.push_line2D(self, line: List[float]) -> None
lines.push_line2D([0, 0, \
                    1, 1, \
                    1, 2])
#in the example above, the vertices get internally stored as:
#input:          a - b - c
#stored values: [0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 2, 0]
#               [a - b, b - c]
#               every 3rd zero is padding for 3D data, inner vertex si duplicated 

#Similar for 3D points
#Attribute.push_line3D(self, line: List[float]) -> None
lines.push_line3D([0, 0, 0,   \
                    1, 1, 0.5, \
                    1, 2, 1])
                    
#@property
#Attribute.geom_type(self) -> int:
assert lines.geom_type == 2

Polygons are automatically triangulated, the API also supports polygons with holes:

from metacity.geometry import Attribute

triangles = Attribute()
#Insert simple 2D polygon using push_polygon3D method:
#Attribute.push_polygon2D(self, polygon: List[List[float]]) -> None
triangles.push_polygon2D([[0, 0, \
                          0, 1, \
                          1, 1, \
                          0, 1]])
                          
#The structure draws from GeoJSON specs,
#the List[List[float]] can be interpreted as [[polygon], [hole], [hole] ...] 
#Insert 2D polygon with a hole in the middle:
triangles.push_polygon2D([[0, 0, \
                          0, 1, \
                          1, 1, \
                          0, 1],\
                          [0.25, 0.25, \
                           0.25, 0.75, \
                           0.75, 0.75, \
                           0.75, 0.25])

#All works equivalently for 3D:
#Attribute.push_polygon3D(self, polygon: List[List[float]]) -> None    
triangles.push_polygon3D([[0, 0, 1, \
                          0, 1, 1, \
                          1, 1, 1, \
                          0, 1, 1]])     
                          
#@property
#Attribute.geom_type(self) -> int:
assert triangles.geom_type == 3                  

Geometry Type

Notice the geom_type property:

from metacity.geometry import Attribute
attribute = Attribute()
#...
#@property
#Attribute.geom_type(self) -> int:
assert attribute.geom_type == 0 #No geometry
assert attribute.geom_type == 1 #Points
assert attribute.geom_type == 2 #Lines
assert attribute.geom_type == 3 #Triangles

Attribute Caveats

As demonstrated above, Attributes can handle loading various types of data. What you should never do is mix those types of data:

#!!!NEVER DO THIS!!!
from metacity.geometry import Attribute
points = Attribute()
points.push_point2D([0, 0, 1, 1, 1, 2])
points.push_line2D([0, 0, 1, 1, 1, 2]) #<- will raise exception

What you can do is mix 2D and 3D data since 2D gets internally padded by zeroes:

from metacity.geometry import Attribute
points = Attribute()
points.push_point2D([0, 0, 1, 1, 1, 2])
points.push_point3D([0, 0, 0, 1, 1, 0, 1, 2, 0]) #<- is allowed

Internally, Attribute keeps track of the first assigned type (points, lines, or polygons) and checks all future assignments against it. If the type does not match, an exception gets raised.

For the encoding explanation, see Attribute . The geom_type property of the Model class always returns the type of the POSITION attribute.

Attributes work similarly to . The Attribute API allows parsing of various data. All data is stored inside as if it was 3D data (2D data gets padded by zeroes).

GLTF buffers
Attributes
Geometry Type