-
-
Notifications
You must be signed in to change notification settings - Fork 64
Spatial Factory Store
The SpatialFactoryStore
is a singleton used to convert geometries stored in your database into RGeo
objects that you can work with in your Ruby application. In most spatial databases, geometries are stored in a format called Well-Known Binary (WKB). This is a way to represent all geometries as hex data and is not really useful in an application. RGeo
comes with a WKBParser
that can be used to parse this text, but the SpatialFactoryStore
allows for this to be done automatically and let the user work on their application not worry about formatting geometries.
The SpatialFactoryStore
works by associating RGeo factories with different attributes used to describe geometry/geography columns. For instance, it allows you to parse all geographic columns with an RGeo::Geographic.spherical_factory
and all geometric columns with an RGeo::Cartesian.preferred_factory
. Or going even further, all line_string
columns could be parsed with a cartesian factory while points use the spherical factory.
By default, the SpatialFactoryStore
has defaults baked-in: all geometry
columns will use RGeo::Cartesian.preferred_factory
and all geography
columns will use RGeo::Geographic.spherical_factory
.
You can override the default factory with the following:
# assign default to whatever factory you want
RGeo::ActiveRecord::SpatialFactoryStore.instance.default = RGeo::Geos.factory(srid: 3857)
So any column that cannot find a match in the registry will fall back to this default.
The SpatialFactoryStore
uses a registry that matches factories to columns by finding the entry with the most matching columns without any mismatches. When column information is sent to the store, it is a hash with the following keys:
geo_type: string # geometry, point, polygon, line_string, geometry_collection,
# multi_line_string, multi_point, multi_polygon
has_m: boolean # true, false
has_z: boolean # true, false
sql_type: string # geometry, geography
srid: int # (any valid SRID)
These are also the keys that can be used when registering a factory to describe what it should match. For example:
RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |store|
# register a factory for all point columns
store.register(RGeo::Cartesian.preferred_factory(srid: 4055), {geo_type: "point"})
# register a factory for all point columns with srid 4326
store.register(RGeo::Cartesian.preferred_factory(srid: 4326), {geo_type: "point", srid: 4326})
end
In this example, here's what the following columns would return:
{geo_type: point, has_m: false,
has_z: false, sql_type: geography,
srid: 3857
}
#=> The first factory because it is a point, but does not have srid 4326, so it mismatches with the second factory.
{geo_type: point, has_m: false,
has_z: false, sql_type: geography,
srid: 4326
}
#=> The second factory because it is a point and has srid 4326, so it has 2 matching attributes with factory2
which is more than factory1 which it has 1 match with.
{geo_type: line_string, has_m: false,
has_z: false, sql_type: geography,
srid: 4326
}
#=> The default factory because it mismatches the geo_type attribute with both and would fall back to the default.
The SpatialFactoryStore
will return the factory with the most matches, but if there are multiple factories tied for the most, the first entry in the registry will be the factory returned.
The best way to set up a registry in Rails is to create your registry in an initializer.
For example:
# config/initializers/rgeo_factories.rb
RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |store|
store.default = RGeo::Cartesian.preferred_factory
store.register(RGeo::Geographic.spherical_factory(srid: 4326), {geo_type: "point", srid: 4326, sql_type: "geography"})
end