Postgres provides a number of geometry types such as a line, a box, a polygon, etc. These types, although a bit primitive, can serve simple GIS systems where curve of the Earth doesn't count. Say, to model buildings and routs across a city.
Recent PG2 versions suppor these types. When you read them from a database, they arrive parsed into Clojure maps and vectors. You can pass them back as Clojure values as well.
A point is a pair of double
values. When read, it becomes as a map with the
:x
and :y
keys:
(pg/query conn "select '(1.01, -2.003)'::point as point")
[{:point {:x 1.01 :y -2.003}}]
Inserting a point:
(pg/query conn "create temp table test1 (id int, p point)")
(pg/execute conn
"insert into test1 (id, p) values ($1, $2)"
{:params [1 {:x 2 :y 3}]})
You can pass it as a vector as well:
(pg/execute conn
"insert into test1 (id, p) values ($1, $2)"
{:params [2 [-3, 6.003]]})
Or as a native SQL string with parentheses:
(pg/execute conn
"insert into test1 (id, p) values ($1, $2)"
{:params [3 "(3.003, -42.01)"]})
Let's check out the table:
(pg/query conn "select * from test1")
[{:id 1, :p {:y 3.0, :x 2.0}}
{:id 2, :p {:y 6.003, :x -3.0}}
{:id 3, :p {:y -42.01, :x 3.003}}]
A box is a pair of points representing its corners, e.g. ((x1, y1), (x2, y2))
. A test table:
(pg/query conn "create temp table test2 (id int, b box)")
Let's insert three boxes at once using various forms:
(pg/execute conn
"insert into test2 (id, b) values ($1, $2), ($3, $4), ($5, $6)"
{:params [1 [{:x 0 :y 0} {:x 4 :y 6}] ;; a vector of maps
2 [[-1 3] [6.003 8.33]] ;; a vector of pairs
3 "((5.003, 5.23),(9.23, -3.555))" ;; a raw SQL form
]})
The result:
(pg/execute conn "select * from test2")
[{:id 1, :b [{:y 6.0, :x 4.0} {:y 0.0, :x 0.0}]}
{:id 2, :b [{:y 8.33, :x 6.003} {:y 3.0, :x -1.0}]}
{:id 3, :b [{:y 5.23, :x 9.23} {:y -3.555, :x 5.003}]}]
Pay attention that Postgres normalizes box's corners. When reading boxes back,
the initial [{:x 0 :y 0} {:x 4 :y 6}]
pair becomes [{:y 6.0, :x 4.0} {:y 0.0, :x 0.0}]
(the points where swapped).
As the docs say:
Lines are represented by the linear equation Ax + By + C = 0, where A and B are not both zero. Values of type line are input and output in the following form: { A, B, C }
In PG2, a line is either a map with :a
, :b
, and :c
keys, or a vector of
three double
values. Prepare a table:
(pg/query conn "create temp table test3 (id int, l line)")
Insert three lines in various forms:
(pg/execute conn
"insert into test3 (id, l) values ($1, $2), ($3, $4), ($5, $6)"
{:params [1 {:a 1.1 :b -2.2 :c 3.3}
2 [-1 -2 -3]
3 "{-9.33, 3.11, -3.555}"]})
The result:
(pg/execute conn "select * from test3")
[{:id 1, :l {:c 3.3, :b -2.2, :a 1.1}}
{:id 2, :l {:c -3.0, :b -2.0, :a -1.0}}
{:id 3, :l {:c -3.555, :b 3.11, :a -9.33}}]
A circle is a point plus its radius: <(-1, 1), 3.3>
. In Clojure, it's either a
map with :x
, :y
, and :r
keys, or a vector of three: [x y r]
. The radius
cannot be negative.
Prepare a table:
(pg/query conn "create temp table test4 (id int, c circle)")
Insert three circles in various forms:
(pg/execute conn
"insert into test4 (id, c) values ($1, $2), ($3, $4), ($5, $6)"
{:params [1 {:x 1.1 :y -2.2 :r 3.3}
2 [-1 -2 3]
3 "<(-9.33, 3.11),3.555>"]})
The result:
(pg/query conn "select * from test4")
[{:id 1, :c {:y -2.2, :r 3.3, :x 1.1}}
{:id 2, :c {:y -2.0, :r 3.0, :x -1.0}}
{:id 3, :c {:y 3.11, :r 3.555, :x -9.33}}]
A polygon is a collection of points surrounded with parentheses:
((1.1,-2.2),(3.3,4.4),(-5.5,6.6))
. A table:
(pg/query conn "create temp table test5 (id int, poly polygon)")
Let's insert polygons. Pay attention that points may follow in various forms: a map, a vector, then a map again:
(pg/execute conn
"insert into test5 (id, poly) values ($1, $2), ($3, $4), ($5, $6)"
{:params [1 [[1 2] {:x 3 :y 4} [5 6]]
2 [{:x 8 :y 3} {:x 5 :y -5}]
3 "((1,2),(3,4),(5,6),(7,8))"]})
The result:
(pg/query conn "select * from test5")
[{:id 1
:poly [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0} {:y 6.0, :x 5.0}]}
{:id 2
:poly [{:y 3.0, :x 8.0} {:y -5.0, :x 5.0}]}
{:id 3
:poly [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0} {:y 6.0, :x 5.0} {:y 8.0, :x 7.0}]}]
A line segment (the lseg
type) is just a pair of points: [(1,2),(3,4)]
. A
table:
(pg/query conn "create temp table test6 (id int, seg lseg)")
Insertion:
(pg/execute conn
"insert into test6 (id, seg) values ($1, $2), ($3, $4), ($5, $6)"
{:params [1 [[1 2] {:x 3 :y 4}]
2 [{:x 8 :y 3} {:x 5 :y -5}]
3 "((1,2),(3,4))"]})
The result:
(pg/execute conn "select * from test6")
[{:id 1, :seg [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0}]}
{:id 2, :seg [{:y 3.0, :x 8.0} {:y -5.0, :x 5.0}]}
{:id 3, :seg [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0}]}]
Paths are a bit tricky. Technically they are collections of points with an
additional flag. A path might be either open (meaning there is no a
connection between the first and the last points) and closed (meaning there
is). Square brackets []
indicate an open path, while parentheses ()
indicate
a closed path.
That extra flag slightly complicates Clojure representation. A path is no longer
a vector of points but a map with the :points
and :closed?
flag. When the
flag is not set, the path is considered as closed.
Prepare a table:
(pg/query conn "create temp table test7 (id int, p path)")
Passing multiple paths in various forms:
(pg/execute conn
"insert into test7 (id, p) values ($1, $2), ($3, $4), ($5, $6), ($7, $8), ($9, $10)"
{:params [1 [[1 -2] {:x 3 :y 4}] ;; a vector of points, closed
2 {:closed? true ;; a map form, closed
:points [[1 2] {:x 3 :y 4}]}
3 {:closed? false ;; a map form, open
:points [[1 2] {:x 3 :y 4}]}
4 "((1,2),(3,4))" ;; SQL form, closed
5 "[(5,4),(3,2)]" ;; SQL form, open
]})
The result:
(pg/execute conn "select * from test7 order by id")
[{:id 1, :p {:points [{:y -2.0, :x 1.0} {:y 4.0, :x 3.0}], :closed? true}}
{:id 2, :p {:points [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0}], :closed? true}}
{:id 3, :p {:points [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0}], :closed? false}}
{:id 4, :p {:points [{:y 2.0, :x 1.0} {:y 4.0, :x 3.0}], :closed? true}}
{:id 5, :p {:points [{:y 4.0, :x 5.0} {:y 2.0, :x 3.0}], :closed? false}}]
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close