How to convert MVT geometry back to lat,lng (4326) using postgis functions? - postgis

Given some arbitrary geometry in the standard ESPG-4326 (latitude & longitude), that was converted to an MVT tile geometry with ST_AsMvtGeom() postgis function (in web mercator 3857), I need to convert it back to the 4326 using postgis functionality.
select st_astext(
st_asmvtgeom(
st_transform(ST_GeomFromText('POINT(-73.985130 40.748817)', 4326), 3857),
st_tileenvelope(8, 75, 96),
extent := 4096, buffer := 0, clip_geom := true));
The above returns POINT(1591 890). I need to write a similar SQL statement to convert it back to POINT(-73.985130 40.748817) using the inputs of 'POINT(1591 890)', 8, 75, 96 (geometry + tile coordinates). The result would obviously be slightly different from the original.

This seems to do the job. The st_tileenvelope() is a bit of an overkill, because it is only needed to compute the width/height of a tile in meters (i.e. 40075000/(2^z)). Also, the last st_transform() step is not really needed - the resulting geometry is already in a usable format and has SRID attached to it.
CREATE OR REPLACE FUNCTION decode_mvt_geom(geom geometry, z int, x int, y int, extent int) RETURNS geometry AS $$
SELECT st_transform(
st_scale(
st_translate(geom, extent * (x - 2 ^ (z - 1)), extent * (y - 2 ^ (z - 1)))
, (st_xmax(st_tileenvelope(z, x, y)) - st_xmin(st_tileenvelope(z, x, y))) / extent
, -(st_ymax(st_tileenvelope(z, x, y)) - st_ymin(st_tileenvelope(z, x, y))) / extent
)
, 4326)
$$ COST 1 LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
A simpler function without the final projection transformation could be this:
CREATE OR REPLACE FUNCTION decode_mvt_geom(geom geometry, z int, x int, y int, extent int) RETURNS geometry AS $$
SELECT st_scale(
st_translate(geom, extent * (x - 2 ^ (z - 1)), -extent * (y - 2 ^ (z - 1))),
40075016.6855785 / (2 ^ z) / extent,
40075016.6855785 / (2 ^ z) / extent
)
$$ COST 1 LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;

Related

How to solve x + ceiling(x) +c = y, for x

I am working on a SQL server project to make conversion between two types of car prices (eg, x is the original price, y is the dealer asking price) based on some rules, so that for every given dealer asking price y, I can get a corresponding original price x. The conversion rule I am having trouble with is this one: x + 5*ceiling(x/100) + some constant c = y, x=?
(It's more of a math problem actually. For example, if the rule is as easy as x + 10 = y, then x = y-10.)
Indeed, it's a math problem, that should be asked on Mathematics Stack Exchange.
That said, note that in the equation x+ceil(x)+c=y, x and y-c differ only by an integer (that depends on x). And since x+ceil(x) is an increasing function of x, if there is a solution it's unique.
Another remark:
for x in (0,1], x+ceil(x) lies in (1,2].
for x in (1,2], x+ceil(x) lies in (3,4].
...
That is, there is a solution only if y-c is in (2n-1,2n] for some integer n. And then, x=y-c-n.
How do we find n? Well, y-c in (2n-1,2n] iff (y-c)/2 in (n-1/2,n]. So we must have n=ceil((y-c)/2).
It's not much more difficult for the equation x+5ceil(x/100)=y-c.
Now,
for x in (0,100], x+5ceil(x/100) in (5,105]
for x in (100,200], x+5ceil(x/100) in (110,210]
...
for x in (100(n-1), 100n], x+5ceil(x/100) in (105n-100,105n]
Again, some values can't be reached, and if there is a solution, it's unique.
And if y-c lies in (105n-100,105n] for some n, then x=y-c-5n.
Of course, you want to find n. Note that y-c in (105n-100,105n] iff (y-c)/105 is in (n-100/105,n], which is a subset of (n-1,n]. So if there is a solution, you must have n=ceil((y-c)/105).
Not a complete answer but for the simple case of
Y = X + Ceiling(X)
declare #X float = 3.7
declare #Y float = 0.0
SELECT #y = #x + ceiling(#x)
SELECT CASE
WHEN (#y -floor(#y)) = 0 THEN #y/2
ELSE (#y - (#y -floor(#y)) - 1)/2 + (#y -floor(#y))
END XValue
This works for all values of X that I've tested. The first case is for when X is a whole integer.
EDIT
For your case of Y = X + 5*CEILING(X)/100 we can surmise that if X is an integer then Y = X + 5/100 * X => 1.05 * X, if X is not an integer then y = X + 5/100(X+1) => 1.05*X + 0.05
So I think the following works for your case:
declare #X float = 3.1
declare #Y float = 0.0
SELECT #y = #x + 5*ceiling(#X)/100
SELECT CASE WHEN (ROUND(#y/1.05,0) = #y/1.05) THEN #y / 1.05
ELSE ROUND((#y - 0.05) / 1.05,2) END
For your case with a constant, I can't see how to solve this without knowing the constant.

what are all the operations that FFMPEG uses to convert from yuv420p to rgb bmp?

I am trying to replicate in my own code, not using ffmpeg libraries, the operations that ffmpeg does to convert yuv420p to rgb. Initially I thought it would be inside the function: xyz12Torgb48 in swscale.c but doing some tracing , it looks to be in yuv2rgb.c ff_yuv2rgb_c_init_tables, which I can not quite see it.
well, since nobody came out with a solution , I am just gonna post that I found using
valgrind tool=callgrind ffmpeg_g
which is a version of ffmpeg with debug objects that showed me the functions being called and inside \libswscale\x86 there is yuv2rgb_template.c which seems to have the operations you do yuv2rgb , in assembly
* Conversion is performed in usual way:
* R = Y' * Ycoef + Vred * V'
* G = Y' * Ycoef + Vgreen * V' + Ugreen * U'
* B = Y' * Ycoef + Ublue * U'
*
* where X' = X * 8 - Xoffset (multiplication is performed to increase
* precision a bit).
* Since it operates in YUV420 colorspace, Y component is additionally
* split into Y1 and Y2 for even and odd pixels.
*
* Input:
* mm0 - U (4 elems), mm1 - V (4 elems), mm6 - Y (8 elems), mm4 - zero register
* Output:
* mm1 - R, mm2 - G, mm0 - B
*/ ```

TSQL - get closest coordinate on linestring to a point

Consider the overly simplistic example: POINT(0 0) and LINESTRING (1 -10, 1 10)
The closest point on the line to the POINT would be 1, 0.
How would one determine this in TSQL? My simple, not entirely accurate, approach was to make a linestring (POINT POINT) and extend out the X coord of one coords until the two linestrings intersected.
So:
linestring (0 0, 0.25 0) (no intersect)
linestring (0 0, 0.5 0) (no intersect)
linestring (0 0, 0.75 0) (no intersect)
linestring (0 0, 1 0) (intersection - so 1 0 is the point closest to POINT
This quasi worked, but doesn't seem to the most bestest/more performant way of accomplishing this.
For example, one inefficiency is that I move it one direction (positive increments), and if there was no match (after x attempts), then I would start over, but with negative increments.
To optimize, I tried moving in larger steps, then when intersected (probably went past the point), I backed off 1 increment and started from there with a smaller increment. I did this a couple of times - instead of going in tiny tiny increments so as not to overshoot by too much.
One acceptable assumption based on my processing that the POINT will be next to (left/right) of the LINESTRING.
Another acceptable assumption is that the LINESTRING will be fairly "perpendicular" to the POINT.
I think you can do this mathematically rather than with a brute-force iterative algorithm.
There is a post to get closest point to a line that describes the method.
I converted this method to SQL which returns the correct value (1,0). Your 'trivial' example is actually a bit of an edge case (vertical line with infinite slope) so it seems robust.
I also tested the source code with this example: https://www.desmos.com/calculator/iz07az84f5 and using the input for the line of (-1,2) (3,0) and a point at (2,2) got the correct answer (1.4, 0.8).
SQL code (also in SQL Fiddle at http://sqlfiddle.com/#!6/d87aa/15)
DECLARE #x int, #y int, #x1 int, #y1 int, #x2 int, #y2 int
DECLARE #atb2 float, #atp_dot_atb float
DECLARE #t float
--SELECT #x=0, #y=0
--SELECT #x1=1, #y1=10, #x2=1, #y2=-10
SELECT #x=2, #y=2
SELECT #x1=-1, #y1=2, #x2=3, #y2=0
SELECT #atb2 = SQUARE(#x2-#x1) + SQUARE(#y2-#y1) -- Basically finding the squared magnitude of a_to_b
SELECT #atp_dot_atb = (#x-#x1)*(#x2-#x1) + (#y-#y1)*(#y2-#y1) -- The dot product of a_to_p and a_to_b
SELECT #t = #atp_dot_atb / #atb2 -- The normalized "distance" from a to your closest point
SELECT #x1 + (#x2-#x1)*#t, #y1 + (#y2-#y1)*#t --Add the distance to A, moving towards B

Superscipt mathematical calculation in SQL Server

Trying to calculate this in SQL Server.
Where c=85, z=4, d=6, and e=4
I tried this
select 85 / 4 * (6 * 4)
but looks like this is wrong any suggest please
Use POWER to handle the exponent:
SELECT 85.0 / (4 * POWER(6, 4))
or
SELECT CAST(85 AS DECIMAL(10, 4)) / (4 * POWER(6, 4))
Or in variable format:
SELECT c / (z * POWER(d, e))
select 85.0 / 4 * power(6,4)
slightly ambiguous formula, does it mean C divided by z then multiplied by d^e - anyway - use SQL x^y = POWER(x,y) and also put 85.0 to make sure floats are used

How to create a circle in meters in postgis?

I would like to ask how to create a circle with radius=4km. I have tried the ST_Buffer function but it creates a larger circle. (I see the created circle by inserting its polygon into an new kml file.)
This is what i am trying.
INSERT INTO camera(geom_circle) VALUES(geometry(ST_Buffer(georgaphy(ST_GeomFromText('POINT(21.304116745663165 38.68607570952619)')), 4000)))
The center of the circle is a lon lat point but I don't know its SRID because I have imported it from a kml file.
Do I need the SRID in order to transform the geometries etc?
KML files are always lat/long and use SRID=4326. This SRID is implied if you use geography. Geography is a good way to mix-in the 4 km metric measure on lat/long data ... excellent you tried this!
Try this statement to fix up the casts, and use a parameterized point constructor:
SELECT ST_Buffer(ST_MakePoint(21.304116745663165, 38.68607570952619)::geography, 4000);
And if you need to cast this back to geometry, add a ::geometry cast to the end.
Update on accuracy
The previous answer internally re-projects the geometry (usually) to a UTM zone that the point fits within (see ST_Buffer). This may cause minor distortions if the point is on the edge of two UTM boundaries. Most folks won't care about the size of these errors, but it will often be several meters. However, if you require sub millimeter precision, consider building a dynamic azimuthal equidistant projection. This requires PostGIS 2.3's ST_Transform, and is adapted from another answer:
CREATE OR REPLACE FUNCTION geodesic_buffer(geom geometry, dist double precision,
num_seg_quarter_circle integer)
RETURNS geometry AS $$
SELECT ST_Transform(
ST_Buffer(ST_Point(0, 0), $2, $3),
('+proj=aeqd +x_0=0 +y_0=0 +lat_0='
|| ST_Y(ST_Centroid($1))::text || ' +lon_0=' || ST_X(ST_Centroid($1))::text),
ST_SRID($1))
$$ LANGUAGE sql IMMUTABLE STRICT COST 100;
CREATE OR REPLACE FUNCTION geodesic_buffer(geom geometry, dist double precision)
RETURNS geometry AS 'SELECT geodesic_buffer($1, $2, 8)'
LANGUAGE sql IMMUTABLE STRICT COST 100;
-- Optional warppers for geography type
CREATE OR REPLACE FUNCTION geodesic_buffer(geog geography, dist double precision)
RETURNS geography AS 'SELECT geodesic_buffer($1::geometry, $2)::geography'
LANGUAGE sql IMMUTABLE STRICT COST 100;
CREATE OR REPLACE FUNCTION geodesic_buffer(geog geography, dist double precision,
num_seg_quarter_circle integer)
RETURNS geography AS 'SELECT geodesic_buffer($1::geometry, $2, $3)::geography'
LANGUAGE sql IMMUTABLE STRICT COST 100;
A simple example to run one of the functions is:
SELECT geodesic_buffer(ST_MakePoint(21.304116745663165, 38.68607570952619)::geography, 4000);
And to compare the distances to each of the buffered points, here are the lengths of each geodesic (shortest path on an ellipsoid of revolution, i.e. WGS84). First this function:
SELECT count(*), min(buff_dist), avg(buff_dist), max(buff_dist)
FROM (
SELECT ST_Distance((ST_DumpPoints(geodesic_buffer(poi, dist)::geometry)).geom, poi) AS buff_dist
FROM (SELECT ST_MakePoint(21.304116745663165, 38.68607570952619)::geography AS poi, 4000 AS dist) AS f
) AS f;
count | min | avg | max
-------+----------------+-----------------+----------------
33 | 3999.999999953 | 3999.9999999743 | 4000.000000001
Compare this to ST_Buffer (first part of answer), that shows it's off by about 1.56 m:
SELECT count(*), min(buff_dist), avg(buff_dist), max(buff_dist)
FROM (
SELECT ST_Distance((ST_DumpPoints(ST_Buffer(poi, dist)::geometry)).geom, poi) AS buff_dist
FROM (SELECT ST_MakePoint(21.304116745663165, 38.68607570952619)::geography AS poi, 4000 AS dist) AS f
) AS f;
count | min | avg | max
-------+----------------+------------------+----------------
33 | 4001.560675049 | 4001.56585986067 | 4001.571105793

Resources