# IFC Coordinate Reference Systems and Revit
Revit has three reference points for coordinates: the internal origin,
a survey point, and a project base point. The project base point may
have an angle to true north set. When you export to IFC, an
`IfcProject` and `IfcSite` is created. Both elements have coordinates
and data. However, are these coordinates correct? Are they complete?
And can we use them reliably in other software? Let's investigate.
## Coordinate systems defined by `IfcProject`
Let's talk about what we want the results to be. According to the IFC
specification, `IfcProject` provides the following information:
- the project coordinate system
- the coordinate space dimension
- the precision used within the geometric representations
- optionally the indication of the true north
- optionally the map conversion between the project coordinate system
and the geospatial coordinate reference system.
This information is provided using the `RepresentationContexts`
relationship of the `IfcProject`. This relationship will contain one
or more `IfcGeometricRepresentationContext` elements. Each will
typically have a `CoordinateSpaceDimension` of 3, to show a 3D model,
and the `Precision` attribute shows the model precision.
The actual project coordinate system is defined by the
`HasCoordinateOperation` relationship. This holds an
`IfcMapConversion` element, which in turn references an
`IfcProjectedCRS`. Let's go through the attributes of
`IfcProjectedCRS` first, as knowing the Coordinate Reference System
(CRS) of the project is the most critical aspect of geeoreferencing:
- `Name`: the `EPSG` code for the CRS. In Sydney, (i.e. GDA94 / MGA
Zone 56) this would be `EPSG:28356`. This should be the only value
you need to fill out if the EPSG number has a complete definition of
the Projected CRS. If the EPSG number is not known, you can enter in
`WKT` as a value here, and full out the subsequent attributes. Just
for the sake of example, let's assume we wanted to describe
`EPSG:28356`.
- `Description`: this would be the description of the CRS, such as
"GDA94 / MGA Zone 56". Note: to reiterate, this, and all subsequent
attributes are optional if you have specified an EPSG number with a
full definition.
- `GeodeticDatum`: following our example above, we can specify it to
be `GDA94`.
- `VerticalDatum`: in Australia, this would be set to `EPSG:5111`,
which represents AHD (Australian Height Datum)
- `MapProjection`: in our example it will be `MGA`.
- `MapZone`: this will be zone `56`, following our Sydney example.
- `MapUnit`: the standard states it is in meters, so we will specify
an `IfcNamedUnit` as such:
`IFCSIUNIT (*, .LENGTHUNIT., $, .METRE.)`.
- It can also nest a `HasCoordinateOperation`, but you really
shouldn't unless you want insanity to occur
Now that the projected CRS is defined, we need to define whether or
not there are any map conversions between our model's local coordinate
system and the map coordinate system. Typically for horizontal
construction projects, such as roads, rail, and other linear
infrastructure, there would be no map conversion, and the local
coordiante system will directly use Eastings and Northings of the map
coordinate system. However, for vertical construction projects, such
as buildings, there will almost always be a map conversion converting
from small, local, XYZ coordinates, to map eastings, northings, and
height coordinates. Here are the attributes of the `IfcMapConversion`:
- `SourceCRS`: refers back to the `IfcGeometricRepresentationContext`
of the `IfcProject` to establish the inverse relationship
- `TargetCRS`: refers to the CRS used in the project. This will hold
an `IfcCoordinateReferenceSystem`, or its subtype `IfcProjectedCRS`.
It has a bit of data:
- `Eastings`: your `IfcProject`'s world
`IfcGeometricRepresentationContext`'s `0,0,0` origin will correlate
to this number. In Sydney, if your building is the Sydney Opera
House, this'll be something like `334902.775`. If you have specified
a `MapUnit` in the `ProjectedCRS` you should use that unit (e.g.
meters). Otherwise, you should use the project units (e.g.
millimeters).
- `Northings`: same as `Eastings`, but for the Y axis. For the Sydney
Opera House, it'll be something like `6252274.139`.
- `OrthogonalHeight`: continuing our example, this'll be the AHD of
our world origin. Wikipedia says it is 4m in elevation, so I guess
it'll be something like `4`. In this case, we keep the same units as
`Eastings` and `Northings`, so that we can apply a uniform scale
afterwords.
- `XAxisAbscissa`: specifies the local X axis vector along the easting
to determine rotation of the local coordinates. If there is no
rotation, this will be `1`.
- `XAxisOrdinate`: specifies the local X axis vector along the
northing to determine rotation of the local coordinates. If there is
no rotation, this will be `0`.
- `Scale`: This is the average _combined scale factor_ across the
small site such that a local surface distance multiplied by this
value equals a map distance. This will typically be something very
close to `1`, but not quite (such as `0.99996`). Your surveyor can
calculate the actual value.
This `IfcMapConversion` and `IfcProjectedCRS` element of the
`IfcProject`'s `IfcGeometricRepresentationContext` holds all of the
georeferencing information that we require. These attributes contains
all of the parameters required to perform a "Helmert transformation",
which is a fancy way of saying how to offset, rotate, and scale local
project coordinates to a globally positioned coordinate system. For
your surveyor to provide these transformation parameters properly,
they will need multiple surveyed points (a minimum of two), ideally
taken at extremes across the site, in both your local coordinates, as
well as their equivalents in the target CRS. They will also need to
know your desired building orientation (i.e. project north) to
calculate the X axis abcissa and ordinate, and a nominated false
origin to set the Eastings and Northings. The more points that are
surveyed, the more accurate this `IfcMapConversion` will become.
With all of the information defined above, to convert from local
coordinates `(X, Y, Z)`, to map grid coordinates `(X', Y', Z')`, you
can use these relationships:

Credits go to
[BuildingSmart Australasia](
https://buildingsmart.org.au/resources/)
who has done great work in clarifying these concepts. Note that I have
clarified the argument order of the `atan2` function is `y, x`, which
is more common in programming languages.
After all of this information is recorded, it's interesting to note
that the `IfcGeometricRepresentationContext` additionally has a
`TrueNorth` attribute. Assuming the `IfcMapConversion` is already
provided, there is actually no need for a `TrueNorth` attribute, and
so if it is provided, it is merely duplicate data and there for
convenience. IFC readers should not parse it and should not apply the
same rotation twice. The `IfcMapConversion` takes priority over the
`TrueNorth` attribute.
## The `WorldCoordinateSystem` attribute
The `IfcGeometricRepresentationContext` of the `IfcProject` also has a
`WorldCoordinateSystem` attribute. Usually, this will be set to
`(0, 0, 0)`, and represents the origin of the _virtual_ world. In
other words, any element in a project usually inherit the local
relative placement of its parent, all the way up to `IfcSite`, but
somewhere, it needs to end in an absolute coordinate. This
`WorldCoordinateSystem` is the final absolute coordinate that is not
relative to anything else. It can therefore be used to offset
everything in your project, should you want to. The `IfcMapConversion`
we just described, will then be used to convert our virtual world into
the real world.
## Coordinate system inheritance
The `IfcSite` is spatially contained in the `IfcProject`. However, the
spatial containment is not the determining factor for how coordinates
are inherited. Instead, the `IfcSite` has an `ObjectPlacement` and a
`Representation` attribute. These are the important attributes to pay
attention to.
The `ObjectPlacement` attribute positions the `IfcSite` element
relative to other objects. We will discuss about different placements
below, but suffice to say that it merely deals with relative offsets
of coordinates.
The `Representation` attribute, however, contains an
`IfcRepresentationContext` chosen from the list of contexts defined at
the `IfcProject` level. It is this particular selection of the
`IfcRepresentationContext` that allows the `IfcSite` to inherit a
particular `WorldCoordinateSystem` and `MapConversion` attribute
defined at the `IfcProject` level.
I would like to emphasize that the coordinate transformation is not
due to spatial containment or hierarchy, but instead due to the
selection of `IfcRepresentationContext`.
In theory, any IFC product that has a representation can select its
own context. This means that an `IfcWall` can have one map conversion,
and have an `ObjectPlacement` to another IFC element which has a
different map conversion. Although technically syntactically correct,
it's considered invalid as it is bad practice and seasoned GIS
professionals will tell you to never mix different CRSes on one map
unless you want to be more artistic than useful.
## Coordinate systems defined by `IfcSite`
In addition to ordinary coordinates, the `IfcSite` provides
`RefLatitude`, `RefLongitutde`, and `RefElevation` attributes. As the
prefix "Ref" suggests, this is a latitude and longitutde provided only
for reference. It is not sufficient for proper geolocation and if
there is a discrepancy between the `IfcMapConversion` and the data
provided in `IfcSite`, the `IfcMapConversion` takes priority.
Note that these `RefLatitude` and `RefLongitude` values are recorded
in integers that are separated by a full stop to represent degrees,
minutes, seconds, and an optional millionths of a second. West and
south locations are negative, and east and north locations are
positive.
In a real project, a project may contain multiple `IfcSite` objects.
Each `IfcSite` has a `Representation`, which may include terrain, for
example. For most projects, there is a site boundary, such as a
cadastral boundary which denotes the legal plot of land. The
`ObjectPlacement` of the `IfcSite` is therefore likely to be a corner
of the site boundary which is a point that has been surveyed.
## Coordinate systems defined by `IfcBuilding`
The `IfcBuilding` contains a `Representation` of the building. It also
contains an `IfcObjectPlacement`, which is relative to the `IfcSite`.
This would place your building on your site model. The rotation of
this placement also sets out the project north of the building. If
your building has multiple wings, it may also define the individual
project norths of each wing.
The `IfcBuilding` additionally contains two attributes:
- `ElevationOfRefHeight`: as one steps into your building, the finish
floor level will be seen as the _building's internal reference
height_ of +0.00. This attribute will record this "+0.00 reference
height" in terms of the absolute values of elevation above sea
level.
- `ElevationOfTerrain`: this is the height in absolute values of
elevation above sea level of the terrain immediately surrounding the
perimeter of the building. If the terrain slopes, it is taken to be
the lowest point.
Just like the reference point values in `IfcSite`, these are also
duplications of data. It is not explicitly mentioned, but I believe
that should there be a discrepancy, the derived coordinate from the
`IfcMapConversion` takes priority.
The `ElevationOfTerrain`, apart from being a reference value, also
provides a datum to measure the `EavesHeight` and the `Height` (total
height) of the building which is recorded in the
`Qto_BuildingBaseQuantities`.
## Absolute coordinates
If your object has an `IfcObjectPlacement`, it usually uses an
`IfcLocalPlacement` which has a `PlacementRelTo`, thus inheriting the
parent's placement. If you omit the `PlacementRelTo`, it does not
inherit any more parent coordinates, and ends up being an absolute
coordinate. An absolute coordinate is defined as only relative to the
`WorldCoordinateSystem` of the `IfcProject`.
A common example for this is the `IfcSite` element which is the
immediate child of the `IfcProject`. Because its only parent
coordinate is the `WorldCoordinateSystem`, it is known as an
_absolute_ placement.
You can also omit the `IfcObjectPlacement` altogether, and it will
therefore also be treated as an absolute placement which is equal to
the `WorldCoordinateSystem` of the `IfcProject`.
Omission of the `IfcObjectPlacement` is a quick and easy way to say
that your `IfcBuildingStorey`, `IfcBuilding`, and `IfcSite`, are all
at the `WorldCoordinateSystem`. This behaviour has been noted in some
software, such as Revit in some circumstances.
Keep in mind that this behaviour is technically possible but it is not
endorsed by buildingSMART. For more information, see this
[ISG implementation agreement CV-2x3-143 agreement on having the containment tree and the relative placement tree identical for spatial elements](
http://www.buildingsmart-tech.org/implementation/ifc-implementation/ifc-impl-agreements/cv-2x3-143).
It is only mentioned out of completeness.
## Coordinate systems and `IfcGrid`
Grids are by convention (i.e. described not enforced by the IFC
specification) placed relative to the local placement of its
container. Grids can be contained by `IfcSite`, `IfcBuilding`, or
`IfcBuildingStorey`. As an alternative to a relative and absolute
placements, coordinates can also be specified constrained to a grid
system. This is as simple as using `IfcGridPlacement` for the
`IfcObjectPlacement`.
Using `IfcGridPlacement` allows you to specify an object's placement
using the intersection of two grid lines, or
`IfcVirtualGridIntersection`, and a direction. The details will not be
described here, but using grids does not change the fundamental
concept of site coordinates. Any object placed this way will still
resolve to a local `(X, Y, Z)` which can be translated using the
`IfcMapConversion`.
It is therefore possible to have an `IfcGrid` spatially contained by
an `IfcSite`. The `IfcBuilding` could then have a placement aligning
with the grid. This makes intuitive sense to lay a building out
relative to a grid placed on the site, but it breaks the ISG agreement
where containment tree and relative placement tree should be
identical.
## _Spatial Composition_ and coordinates
For the objects that we've described so far, they usually use the
_Spatial Composition_ concept to relate to one another. Keep in mind
that spatial decomposition and inheritance of coordinates are two
separate concepts. Just because something is spatially contained in a
parent container does _not_ mean that it inherits its coordinates.
However, that being said, a convention is endorsed by the
specification's
[documentation of `IfcLocalPlacement`](
https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcgeometricconstraintresource/lexical/ifclocalplacement.htm)
to place objects relative to the same container that it is spatially
contained in. I've linked the page for you to read the details of the
relationships that are endorsed.
## Coordinate systems created by Revit
Now that we know what should be defined and how they should be defined
in an ideal world, let's take a Revit project as an example. We will
be
[exporting using the open source Revit exporter as an IFC4 file](../how-to-create-better-ifc-files-with-revit.html).
Revit provides an `IfcProject` as expected, and this has one
`IfcGeometricRepresentationContext`. Unfortunately, our luck ends
here, as no `IfcMapConversion` is provided. Therefore, all that great
stuff we talked about where we could record a CRS, its datums and
projections, and so on, are not available.
Let's see what information we _do_ have in the `IfcProject`'s
`IfcGeometricRepresentationContext`:
- `Precision`: this is set to 0.01 of a millimeter in my metric
scenario.
- `WorldCoordinateSystem`: this is set to `(0, 0, 0)`. As far as I
have observed, this never changes, regardless of the placement of
any of Revit's origins.
- `TrueNorth`: this represents a vector of the "Angle to True North"
value that you have set for your project base point. For example, if
your project has no rotation, it will be the vector `(0, 1)`. If
your true north is set to 45 degrees (i.e. top left of your
building), then it will be the vector `(-Pi/4, Pi/4)` (in the second
quadrant). And so on.
An `IfcSite` is also provided with the following information.
- `RefLatitude`: this value is taken from your Revit project's
"Location" latitude, and has no relationship to the survey point or
project base point. There are a few quirks to watch out for: it is
comma and space separated instead of using a full stop separator,
and it records to a degree of precision that may return different
results if you are using other programs that do not share the same
degree of precision.
- `RefLongitude`: same as latitude, but for longitude instead.
- `RefElevation`: this value is set to the `Elev` field of the Revit
Project Base Point.
The final information we can extract is the `ObjectPlacement`
attribute of the `IfcSite` element. This has a location and a
direction. The direction is identical to the `TrueNorth` value
described above, but has a Z dimension too. The Z dimension may be set
to the `Elev` field of the Revit Project Base Point, if the "Include
IFCSITE elevation in the site local placement origin" option is
checked in the "Advanced" tab of the IFC export settings. If the
option is not checked, it is set to `0`.
Credits to [Niel Simmonds](
https://www.clearboxbim.com/) for
clarifying the behaviour of elevation data.
The `Location` of the `IfcSite`, however, is a bit of a tricky one. It
is a 3D coordinate, with the Z ordinate always set to `0`. The X and Y
ordinates have a relationship to the project base point (PBP) as shown
below. Revit further complicates things by having a project base point
that can be represented by eastings and northings that are relative to
either the survey point, or the project's invisible internal origin
point. These are denoted with the subscript `Survey` and `Origin`
respectively.

The `IfcBuilding` is always set to a relative placement of
`(0, 0, 0)`, and therefore fully inherits the location of the
`IfcSite`. However, I have seen elevation data at the
`IfcBuildingStorey` level, set in its `ObjectPlacement`'s `Location` Z
ordinate (X and Y are hardcoded at `0, 0`). In particular, this Z
value will not be the value you set in your Revit level schedule.
Instead, it will be your Revit level value plus the `Elev` value of
your Project Base Point. As a result, your building will always have
"absolute" height values in its IFC export, even though it may not be
inherited or stored at the location recommended by the IFC spec. I
should note that the `Elev` value set on your Revit Survey Point has
no impact whatsoever on the IFC export.
It should be noted that
[Revit does not know what a building or site is](../why-revit-is-shit/article.md)
and so it is not possible to have multiple buildings, building
portions, sites, or sub-sites. All files automatically generate and
contain all of their contents inside a single site and single
building.
Knowing this information is extremely important. Because Revit does
not specify a CRS conversion, many people put their local CRS eastings
and northings coordinates into the project base point and the survey
point. As most of these values are very large in magnitude, the
`IfcSite` local placement will too be extremely far off in space.
This far site placement is inherited by every single object spatially
contained by the site. Any program which then parses the IFC to
extract the geometry will have to deal with this, and the inability to
deal with such large transformation magnitudes is a well known problem
in the 3D industry. As a result, geometry gets unnecessarily "broken"
and imports are unreliable. You can experience this yourself by
importing an IFC into Blender which has CRS coordinates, or even
attempting to import an IFC back into Revit: it won't be able to
handle its own creation.
As a workaround, you can simply modify the `IfcLocalPlacement` of the
`IfcSite` in a text editor before importing the IFC. After the import,
you can then specify using native GIS features of your application how
the origin point translates to a target CRS. Knowing the relationship
of the `IfcSite` coordinates above will help you specify the origin
correctly. I tend to simply reset the value such that the origin point
corresponds with the project base point. In simple scenarios this is
`(0, 0, 0)`, but sometimes it may have a further offset which you can
calculate using the formula above. An alternative, simpler way is by
finding the difference in coordinates between the `IfcSite` and
Revit's project base point, and making that the new `IfcSite` local
placement `Location`.
This article should clarify all coordinate related concepts in BIM and
Revit, and hopefully you can now use IFCs a bit more reliably across
other software other than Revit.