PostGIS make buffer on LINESTRING Z to have a POLYGON Z - postgresql

I have several LINESTRING Z geometies in PostgreSQL and they look like
LINESTRING Z (1 2 1,1 1 4)
I want to make a buffer around this linestring so that i can have a POLYGON Z geometry for further export to dxf.
I tried this
select st_astext(st_buffer('LINESTRING Z (1 2 1,1 1 4)'::geometry, 2)) as geom;
and it gives me
POLYGON((3 1,2.96157056080646 0.609819355967741,2.84775906502257 0.23463313526
9818,2.66293922460509 -0.111140466039206,2.41421356237309 -0.414213562373096,2.
1111404660392 -0.662939224605091,1.76536686473018 -0.847759065022574,1.39018064
403226 -0.961570560806461,1 -1,0.609819355967745 -0.961570560806461,0.234633135
269822 -0.847759065022574,-0.111140466039202 -0.662939224605092,-0.414213562373
094 -0.414213562373096,-0.662939224605089 -0.111140466039207,-0.847759065022572
0.234633135269818,-0.96157056080646 0.609819355967739,-1 1,-1 2,-0.96157056080
646 2.39018064403226,-0.847759065022572 2.76536686473018,-0.662939224605089 3.1
1114046603921,-0.414213562373094 3.4142135623731,-0.111140466039203 3.662939224
60509,0.234633135269821 3.84775906502257,0.609819355967744 3.96157056080646,1 4
,1.39018064403226 3.96157056080646,1.76536686473018 3.84775906502257,2.11114046
60392 3.66293922460509,2.41421356237309 3.4142135623731,2.66293922460509 3.1111
4046603921,2.84775906502257 2.76536686473018,2.96157056080646 2.39018064403226,
3 2,3 1)) (1 row)
which is in 2D POLYGON not POLYGON Z
How can I make it 3D?

I'm not totally sure what you want to achieve, but did you take a look at ST_Force3D?
SELECT
ST_AsText(
ST_Force3D(
ST_Buffer('LINESTRING Z (1 2 1,1 1 4)'::GEOMETRY, 2)));
It will return a POLYGON Z geometry:
POLYGON Z ((3 1 0,2.96157056080646 0.609819355967741 0,2.84775906502257 0.234633135269818 0,2.66293922460509 -0.111140466039206 0,2.41421356237309 -0.414213562373096 0,2.1111404660392 -0.662939224605091 0,1.76536686473018 -0.847759065022574 0,1.39018064403226 -0.961570560806461 0,1 -1 0,0.609819355967745 -0.961570560806461 0,0.234633135269822 -0.847759065022574 0,-0.111140466039202 -0.662939224605092 0,-0.414213562373094 -0.414213562373096 0,-0.662939224605089 -0.111140466039207 0,-0.847759065022572 0.234633135269818 0,-0.96157056080646 0.609819355967739 0,-1 1 0,-1 2 0,-0.96157056080646 2.39018064403226 0,-0.847759065022572 2.76536686473018 0,-0.662939224605089 3.11114046603921 0,-0.414213562373094 3.4142135623731 0,-0.111140466039203 3.66293922460509 0,0.234633135269821 3.84775906502257 0,0.609819355967744 3.96157056080646 0,1 4 0,1.39018064403226 3.96157056080646 0,1.76536686473018 3.84775906502257 0,2.1111404660392 3.66293922460509 0,2.41421356237309 3.4142135623731 0,2.66293922460509 3.11114046603921 0,2.84775906502257 2.76536686473018 0,2.96157056080646 2.39018064403226 0,3 2 0,3 1 0))
The function ST_Buffer discards the Z dimension, as stated in the documentation:
... This function ignores the third dimension (z) and will always give a
2-d buffer even when presented with a 3d-geometry.
EDIT:
This query sort of creates a buffer with the average Z value of a given LINESTRING Z.
WITH j AS (
SELECT
ST_DumpPoints(
ST_Buffer('LINESTRING Z (1 2 1,1 1 4)'::GEOMETRY, 2)
) AS pt,
(SELECT AVG(z) AS avg_z
FROM (SELECT ST_Z((ST_DumpPoints('LINESTRING Z (1 2 1,1 1 4)'::GEOMETRY)).geom) AS z) AS z) AS lsz
)
SELECT ST_AsText(
ST_MakePolygon(ST_MakeLine(ST_MakePoint(ST_X((pt).geom),ST_Y((pt).geom),lsz))))
FROM j
GROUP BY lsz;
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
POLYGON Z ((3 1 2.5,2.96157056080646 0.609819355967741 2.5,2.84775906502257 0.234633135269818 2.5,2.66293922460509 -0.111140466039206 2.5,2.41421356237309 -0.414213562373096 2.5,2.1111404660392 -0.662939224605091 2.5,1.76536686473018 -0.847759065022574 2.5,1.39018064403226 -0.961570560806461 2.5,1 -1 2.5,0.609819355967745 -0.961570560806461 2.5,0.234633135269822 -0.847759065022574 2.5,-0.111140466039202 -0.662939224605092 2.5,-0.414213562373094 -0.414213562373096 2.5,-0.662939224605089 -0.111140466039207 2.5,-0.847759065022572 0.234633135269818 2.5,-0.96157056080646 0.609819355967739 2.5,-1 1 2.5,-1 2 2.5,-0.96157056080646 2.39018064403226 2.5,-0.847759065022572 2.76536686473018 2.5,-0.662939224605089 3.11114046603921 2.5,-0.414213562373094 3.4142135623731 2.5,-0.111140466039203 3.66293922460509 2.5,0.234633135269821 3.84775906502257 2.5,0.609819355967744 3.96157056080646 2.5,1 4 2.5,1.39018064403226 3.96157056080646 2.5,1.76536686473018 3.84775906502257 2.5,2.1111404660392 3.66293922460509 2.5,2.41421356237309 3.4142135623731 2.5,2.66293922460509 3.11114046603921 2.5,2.84775906502257 2.76536686473018 2.5,2.96157056080646 2.39018064403226 2.5,3 2 2.5,3 1 2.5))
(1 row)

Related

Maxima. How to prevent degree calculations

Is it possible for all calculations in the expression for numbers in a power to be prevented? Perhaps by pre-processing the expression or adding tellsimp rules? Or some other way?
For example, to
distrib (10 ^ 10 * (x + 1));
which produces:
1000000000 x + 1000000000
instead issued:
10 ^ 10 * x + 10 ^ 10
And similarly
factor (10 ^ 10 * x + 10 ^ 10);
returned:
10 ^ 10 * (x + 1);
Just as
factor(200);
2^3*5^2
represents power of numbers, only permanently?
Interesting question, although I don't see a good solution. Here's something I tried as an experiment, which is to display integers in factored form. I am working with Maxima 5.44.0 + SBCL.
(%i1) :lisp (defun integer-formatter (x) ($factor x))
INTEGER-FORMATTER
(%i1) :lisp (setf (get 'integer 'formatter) 'integer-formatter)
INTEGER-FORMATTER
(%i1) (x + 1000)^3;
3 3 3
(%o1) (x + 2 5 )
(%i2) 10^10*(x + 1);
2 5 2 5
(%o2) (2 5 ) (x + 1)
This is only a modification of the display; the internal representation is just a single integer.
(%i3) :lisp $%
((MTIMES SIMP) 10000000000 ((MPLUS SIMP) 1 $X))
That seems kind of clumsy, since e.g. 2^(2*5)*5^(2*5) isn't really more comprehensible than 10000000000.
A separate question is whether the arithmetic on 10^10 could be suppressed, so it actually stays as 10^10 and isn't represented internally as 10000000000. I'm pretty sure that would be difficult. Unfortunately Maxima is not too good with retracting identities which are applied, particularly with the built-in identities which are applied to perform arithmetic and other operations.

How does this kbd function work?

Would someone please explain the below function? How does the kdb utilize x here?
I changed the original function to "func" I know it uses the queries assigned in que to query both functions. The problem is I don't know how those x 0 and and x 0 1 work here. I know 0 is the index 1 and 1 is the index 2 and x is the parameter.
svcs:`TEST:1`TEST:2
que: (`$"select count sym from trades";`$"select sum size from trades")
{func[x 0;string x 1],x 0 1}each svcs cross que
x is being passed in as a two element list "each" time the function is called.
Forget about the func description for now and just add in some logging to see things more clearly:
q)svcs cross que
TEST:1 select count sym from trades
TEST:1 select sum size from trades
TEST:2 select count sym from trades
TEST:2 select sum size from trades
q){show enlist "x 0 is ",string x 0;show enlist "x 1 is ",string x 1;show raze "
x 0 1 is ",(string x 0)," & ", (string x 1)}each svcs cross que;
"x 0 is TEST:1"
"x 1 is select count sym from trades"
"x 0 1 is TEST:1 & select count sym from trades"
"x 0 is TEST:1"
"x 1 is select sum size from trades"
"x 0 1 is TEST:1 & select sum size from trades"
"x 0 is TEST:2"
"x 1 is select count sym from trades"
"x 0 1 is TEST:2 & select count sym from trades"
"x 0 is TEST:2"
"x 1 is select sum size from trades"
"x 0 1 is TEST:2 & select sum size from trades"
Or an individual example:
q)x:(`TEST:1;`$"select count sym from trades")
q)x 0
`TEST:1
q)x 1
`select count sym from trades
q)x 0 1
`TEST:1`select count sym from trades
x 0 1 indexes and retrieves both the first and second element (returning
You're right and x 0 or x 1 is the same as x[0] or x[1] (see the link below on indexing lists), except perhaps the problem is that it isn't obvious what x is.
Because of the adverb each the function is executed four times, once for each item in svcs cross que
See;
q)count svcs cross que
4
And then each item in svcs cross que has two items.
q)count each svcs cross que
2 2 2 2
So in the first execution, x is the first item of svcs cross que and so on.
q)first svcs cross que
`TEST:1`select count sym from trades
q)(first svcs cross que) 0
`TEST:1
q)(first svcs cross que) 1
`select count sym from trades
q)(first svcs cross que) 0 1
`TEST:1`select count sym from trades
Reference
http://code.kx.com/q4m3/6_Functions/#671-monadic-each
http://code.kx.com/q4m3/3_Lists/#34-indexing

How to check 3D coordinates against 3D bounding box in PostGIS?

I would have imagined the obvious query was:
postgres=# SELECT ST_GeomFromText( 'POINT( 1 2 3 )' ) &&&
'BOX3D( -5 -5 -5, 5 5 5 )'::box3d;
But this results in
?column?
----------
f
As opposed to t.
The query seems to lose the z-coordinate from the bounding box completely. This also results in the following issue where a bounding box ranging from z=1 to z=2 will return t for a point at z=0:
galaxymap=# SELECT ST_GeomFromText( 'POINT( 0 0 0 )' ) &&&
'BOX3D( -1 -1 1, 1 1 2 )'::box3d;
?column?
----------
t
(1 row)
After an hour of googling I finally happened upon an e-mail conversation on the postgis-devel mailing list.
Our boxes are all broken.
There should be somewhere a wiki page or ticker or something about
options to improve the situation.
The suggested workaround seems to be using lines (or bounding diagonals, which I didn't try):
SELECT ST_MakePoint( 1, 2, 3 ) &&& ST_MakeLine(
ST_MakePoint( -10, -10, -10 ), ST_MakePoint( 10, 10, 10 ) );
?column?
----------
t

PostGIS: intersections of set of collinear line segments, with counts

I have a set of collinear line segments (may be mutually disjoint, contained, or overlapping).
I want to make a new set of line segments where the segments are disjoint or touching (not overlapping), and each line segment has a count of the original line segments that cover it.
For example, suppose the original set is (drawn non-collinearly for illustration):
A----------------------B
C---------------------------D
E-----F
G-------------H
I-------J
the desired new set would be:
A-------C---E-----F-----B-----------D G-------------H-------J
1 2 3 2 1 1 1
(only the point coordinates matter, the new set does not share point objects with the old set)
How can I achieve this with PostGIS?
Related question: suppose I start with a table of line segments, not all collinear, how do I write the entire query that groups the collinear segments together and then applies the solution to my first question?
Thanks for any help!
Setup (for later queries):
create table lines (
id serial primary key,
label text not null,
line_data geometry(linestring) not null
);
insert into lines(label, line_data)
values ('A-B', ST_MakeLine(ST_MakePoint(-3, -6), ST_MakePoint( 1, 2))),
('D-C', ST_MakeLine(ST_MakePoint( 2, 4), ST_MakePoint(-2, -4))),
('E-F', ST_MakeLine(ST_MakePoint(-1, -2), ST_MakePoint( 0, 0))),
('G-H', ST_MakeLine(ST_MakePoint( 3, 6), ST_MakePoint( 4, 8))),
('I-J', ST_MakeLine(ST_MakePoint( 4, 8), ST_MakePoint( 5, 10))),
('P-L', ST_MakeLine(ST_MakePoint( 1, 0), ST_MakePoint( 2, 2))),
('X-Y', ST_MakeLine(ST_MakePoint( 2, 2), ST_MakePoint( 0, 4)));
Notes:
I purposely switched your D and C points to demonstrate a need for vector negation
The P-L line is parallel with your example lines (but not collinear)
The X-Y line has nothing to do with the others
the solutions below obviously won't work, when you have linestrings that have more than 2 points and those are not on the same line (so when a single linestring is not straight).
The ST_Union aggregate function can split your collinear linestrings. You'll just need to calculate how many lines are containing those.
However, grouping by collinearity is not that simple. I did not find any out-of-the-box solution for this, but you can calculate it (this will not calculate counts yet):
select string_agg(label, ','), ST_AsText(ST_Multi(ST_Union(line_data)))
from lines
group by (
select case
when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
else (
select row(
ST_SRID(s),
(select case
when ST_Y(rv) < 0
then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
else rv
end), -- normalized vector (negated when necessary, but same for all parallel lines)
(ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
)
from coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
)
end
from ST_StartPoint(line_data) s,
ST_EndPoint(line_data) e
)
will produce:
X-Y | MULTILINESTRING((2 2,0 4))
P-L | MULTILINESTRING((1 0,2 2))
E-F,A-B,I-J,G-H,D-C | MULTILINESTRING((-3 -6,-2 -4),(-2 -4,-1 -2),(-1 -2,0 0),(0 0,1 2),(2 4,1 2),(3 6,4 8),(4 8,5 10))
To calculate counts, JOIN your original data again, where the splitted lines are contained by (ST_Contains) your original lines:
select ST_AsText(splitted_line), count(line_data)
from (select ST_Multi(ST_Union(line_data)) ml
from lines
group by (
select case
when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
else (
select row(
ST_SRID(s),
(select case
when ST_Y(rv) < 0
then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
else rv
end), -- normalized vector (negated when necessary, but same for all parallel lines)
(ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
)
from coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
)
end
from ST_StartPoint(line_data) s,
ST_EndPoint(line_data) e)) al,
generate_series(1, ST_NumGeometries(ml)) i,
ST_GeometryN(ml, i) splitted_line
left join lines on ST_Contains(line_data, splitted_line)
group by splitted_line
will return:
LINESTRING(-3 -6,-2 -4) | 1
LINESTRING(-2 -4,-1 -2) | 2
LINESTRING(-1 -2,0 0) | 3
LINESTRING(0 0,1 2) | 2
LINESTRING(2 2,0 4) | 1
LINESTRING(1 0,2 2) | 1
LINESTRING(2 4,1 2) | 1
LINESTRING(3 6,4 8) | 1
LINESTRING(4 8,5 10) | 1

Crystal Reports Cross-tab with mix of Sum, Percentages and Computed values

Being new to crystal, I am unable to figure out how to compute rows 3 and 4 below.
Rows 1 and 2 are simple percentages of the sum of the data.
Row 3 is a computed value (see below.)
Row 4 is a sum of the data points (NOT a percentage as in row 1 and row 2)
Can someone give me some pointers on how to generate the display as below.
My data:
2010/01/01 A 10
2010/01/01 B 20
2010/01/01 C 30
2010/02/01 A 40
2010/02/01 B 50
2010/02/01 C 60
2010/03/01 A 70
2010/03/01 B 80
2010/03/01 C 90
I want to display
2010/01/01 2010/02/01 2010/03/01
========== ========== ==========
[ B/(A + B + C) ] 20/60 50/150 80/240 <=== percentage of sum
[ C/(A + B + C) ] 30/60 60/150 90/240 <=== percentage of sum
[ 1 - A/(A + B + C) ] 1 - 10/60 1 - 40/150 1 - 70/240 <=== computed
[ (A + B + C) ] 60 150 250 <=== sum
Assuming you are using a SQL data source, I suggest deriving each of the output rows' values (ie. [B/(A + B + C)], [C/(A + B + C)], [1 - A/(A + B + C)] and [(A + B + C)]) per date in the SQL query, then using Crystal's crosstab feature to pivot them into the output format desired.
Crystal's crosstabs aren't particularly suited to deriving different calculations on different rows of output.