You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+291Lines changed: 291 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,297 @@ development:
22
22
user: <username>
23
23
```
24
24
25
+
## Working with Spatial Data
26
+
27
+
The adapter uses [RGeo](https://github.com/rgeo/rgeo) and [RGeo-ActiveRecord](https://github.com/rgeo/rgeo-activerecord) to represent geometric and geographic data as Ruby objects and easily interface them with the adapter. The following is a brief introduction to RGeo and tips to help setup your spatial application. More documentation about RGeo can be found in the [YARD Docs](https://rubydoc.info/github/rgeo/rgeo) and [wiki](https://github.com/rgeo/rgeo/wiki).
28
+
29
+
### Installing RGeo
30
+
31
+
RGeo can be installed with the following command:
32
+
33
+
```sh
34
+
gem install rgeo
35
+
```
36
+
37
+
The best way to use RGeo is with GEOS support. If you have a version of libgeos installed, you can check that it was properly linked with RGeo by running the following commands:
38
+
39
+
```rb
40
+
require'rgeo'
41
+
42
+
RGeo::Geos.supported?
43
+
#=> true
44
+
```
45
+
46
+
If this is `false`, you may need to specify the GEOS directory while installing. Here's an example linking it to the CockroachDB GEOS binary.
RGeo uses [factories](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)) to create geometry objects and define their properties. Different factories define their own implementations for standard methods. For instance, the `RGeo::Geographic.spherical_factory` accepts latitudes and longitues as its coordinates and does computations on a spherical surface, while `RGeo::Cartesian.factory` implements geometry objects on a plane.
55
+
56
+
The factory (or factories) you choose to use will depend on the requirements of your application and what you need to do with the geometries they produce. For example, if you are working with points or other simple geometries across long distances and need precise results, the spherical factory is a good choice. If you're working with polygons or multipolygons and analyzing complex relationships between them (`intersects?`, `difference`, etc.), then using a cartesian factory backed by GEOS is a much better option.
57
+
58
+
Once you've selected a factory, you need to create objects. RGeo supports geometry creation through standard constructors (`point`, `line_string`, `polygon`, etc.) or by WKT and WKB.
The default spatial factory for geographic columns is `RGeo::Geographic.spherical_factory`.
197
+
The default spatial factory for cartesian columns is `RGeo::Cartesian.preferred_factory`.
198
+
You do not need to configure the `SpatialFactoryStore` if these defaults are ok.
199
+
200
+
More information about configuration options for the `SpatialFactoryStore` can be found in the [rgeo-activerecord](https://github.com/rgeo/rgeo-activerecord#spatial-factories-for-columns) docs.
201
+
202
+
### Reading and Writing Spatial Columns
203
+
204
+
When you access a spatial attribute on your ActiveRecord model, it is given to
205
+
you as an RGeo geometry object (or nil, for attributes that allow null
206
+
values). You can then call the RGeo api on the object. For example, consider
207
+
the MySpatialTable class we worked with above:
208
+
209
+
```rb
210
+
record =MySpatialTable.find(1)
211
+
point = record.lonlat # Returns an RGeo::Feature::Point
212
+
p point.x # displays the x coordinate
213
+
p point.geometry_type.type_name # displays "Point"
214
+
```
215
+
216
+
The RGeo factory for the value is determined by how you configured the
217
+
ActiveRecord class, as described above. In this case, we explicitly set a
218
+
spherical factory for the `:lonlat` column:
219
+
220
+
```rb
221
+
factory = point.factory # returns a spherical factory
222
+
```
223
+
224
+
You can set a spatial attribute by providing an RGeo geometry object, or by
225
+
providing the WKT string representation of the geometry. If a string is
226
+
provided, the adapter will attempt to parse it as WKT and
227
+
set the value accordingly.
228
+
229
+
```rb
230
+
record.lonlat ='POINT(-122 47)'# sets the value to the given point
231
+
```
232
+
233
+
If the WKT parsing fails, the value currently will be silently set to nil. In
234
+
the future, however, this will raise an exception.
235
+
236
+
```rb
237
+
record.lonlat ='POINT(x)'# sets the value to nil
238
+
```
239
+
240
+
If you set the value to an RGeo object, the factory needs to match the factory
241
+
for the attribute. If the factories do not match, the adapter
242
+
will attempt to cast the value to the correct factory.
243
+
244
+
```rb
245
+
p2 = factory.point(-122, 47) # p2 is a point in a spherical factory
246
+
record.lonlat = p2 # sets the value to the given point
247
+
record.shape1 = p2 # shape1 uses a flat geos factory, so it
248
+
# will cast p2 into that coordinate system
249
+
# before setting the value
250
+
record.save
251
+
```
252
+
253
+
If you attempt to set the value to the wrong type, such as setting a linestring attribute to a point value, you will get an exception from the database when you attempt to save the record.
254
+
255
+
```rb
256
+
record.path = p2 # This will appear to work, but...
257
+
record.save # This will raise an exception from the database
258
+
```
259
+
260
+
### Spatial Queries
261
+
262
+
You can create simple queries based on representational equality in the same
Note that these queries use representational equality, meaning they return
276
+
records where the lonlat value matches the given value exactly. A 0.00001
277
+
degree difference would not match, nor would a different representation of the
278
+
same geometry (like a multi_point with a single element). Equality queries
279
+
aren't generally all that useful in real world applications. Typically, if you
280
+
want to perform a spatial query, you'll look for, say, all the points within a
281
+
given area. For those queries, you'll need to use the standard spatial SQL
282
+
functions provided by PostGIS.
283
+
284
+
To perform more advanced spatial queries, you can use the extended Arel interface included in the adapter. The functions accept WKT strings or RGeo features.
285
+
286
+
```rb
287
+
point =RGeo::Geos.factory(srid:0).point(1,1)
288
+
289
+
# Example Building model where geom is a column of polygons.
See the [rgeo-activerecord YARD Docs](https://rubydoc.info/github/rgeo/rgeo-activerecord/RGeo/ActiveRecord/SpatialExpressions) for a list of available PostGIS functions.
295
+
296
+
### Validation Issues
297
+
298
+
If you see an `RGeo::Error::InvalidGeometry (LinearRing failed ring test)` message while loading data or creating geometries, this means that the geometry you are trying to instantiate is not topologically valid. This is usually due to self-intersections in the geometry. The default behavior of RGeo factories is to raise this error when an invalid geometry is being instansiated, but this can be ignored by setting the `uses_lenient_assertions` flag to `true` when creating your factory.
wkt ="POLYGON (0 0, 1 1, 0 1, 1 0, 0 0)"# closed ring with self intersection
305
+
306
+
regular_fac.parse_wkt(wkt)
307
+
#=> RGeo::Error::InvalidGeometry (LinearRing failed ring test)
308
+
309
+
p modified_fac.parse_wkt(wkt)
310
+
#=> #<RGeo::Geographic::SphericalPolygonImpl>
311
+
```
312
+
313
+
Be careful when performing calculations on potentially invalid geometries, as the results might be nonsensical. For example, the area returned of an hourglass made of 2 equivalent triangles with a self-intersection in the middle is 0.
314
+
315
+
Note that when using the `spherical_factory`, there is a chance that valid geometries will be interpreted as invalid due to floating point issues with small geometries.
0 commit comments