How to solve the lack of precision of the coordinates of the centroid points after a buffer? - geopandas

When generating polygons by buffer (here squares), the geometric points used for generation have different coordinates than those taken by the .centroid method on the polygon after their generation.
Here is an example with just one point.
from shapely.ops import transform
import geopandas as gpd
import shapely.wkt
import pyproj
from math import sqrt
def edge_size(area): return sqrt(area)*1e3
point = "POINT (4379065.583907348 2872272.254645019)"
point = shapely.wkt.loads(point)
center = gpd.GeoSeries(point)
project = pyproj.Transformer.from_proj(
pyproj.Proj('epsg:3395'),
pyproj.Proj('epsg:4326'),
always_xy=True)
center = center.apply(lambda p: transform(project.transform, p))
print(center.iloc[0])
square = point.buffer(
edge_size(3), cap_style=3) #distance of 3km2
square = gpd.GeoSeries(square)
square = square.apply(lambda p: transform(project.transform, p))
square = square.apply(lambda p: p.centroid)
print(square.iloc[0])
#POINT (39.33781544185747 25.11929860805248)
#POINT (39.33781544185747 25.11929777802279)
This leads to processing errors afterwards.
First of all, is this normal? And how to solve this problem?
I also reported my problem here. Thank you for your attention.

Copying my answer from GitHub for posterity.
This is not a bug but a misunderstanding of coordinate transformation. You have to keep in mind that what is square in one projection is not square in another.
If you stick to the same CRS, the output of the centroid of a buffer equals the initial point. But the centroid of a reprojected polygon is slightly off, specifically because you did reprojection that skewed the geometry in one direction.
How to overcome this problem?
Do all your operations in one CRS and reproject once you are done.

Related

rotation in frequency domain - question about question 64441200

Following : How to rotate a non-squared image in frequency domain
I just ran this exact code (copy pasted from the author's code with just an additional normalization of the resulting image between 0 an 255 as shown)
... but I get horrible "aliasing" artifacts ... How is this possible ? I see that the OP shows nice unartifacted images from the rotation in frequency space... I would be very curious to know how to obtain that, surely you did not show all your code?
import numpy as np
import cv2
from numpy.fft import fftshift as fftshift
from numpy.fft import ifftshift as ifftshift
angle = 30
M = cv2.imread("phantom.png")
M = cv2.cvtColor(M, cv2.COLOR_BGR2GRAY)
M=np.float32(M)
hanning=cv2.createHanningWindow((M.shape[1],M.shape[0]),cv2.CV_32F)
M=hanning*M
sM = fftshift(M)
rotation_center=(M.shape[1]/2,M.shape[0]/2)
rot_matrix=cv2.getRotationMatrix2D(rotation_center,angle,1.0)
FsM = fftshift(cv2.dft(sM,flags = cv2.DFT_COMPLEX_OUTPUT))
rFsM=cv2.warpAffine(FsM,rot_matrix,(FsM.shape[1],FsM.shape[0]),flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
IrFsM = ifftshift(cv2.idft(ifftshift(rFsM),flags=cv2.DFT_REAL_OUTPUT))
x = IrFsM
x = ((x-np.min(x[:]))/(np.max(x[:])-np.min(x[:])))*255.0
cv2.imwrite('rotated_phantom.png',x)
the output image is:
Also, i ve always been told that it was impossible to correctly rotate (in discrete, not continuous) Fourier space because of interpolation, so how can you explain that?
The DFT imposes a periodicity to the image in both the frequency and the spatial domain (some people disagree with this, but still agree that this view is a good way to explain just about everything that happens in the DFT...). So imagine that your input is not the Shepp-Logan phantom, but an infinite repetition of it. When manipulating the data in the frequency domain, you affect not just the one copy of the image you see, but all of them, and not always in intuitive ways.
One of the consequences is that neighboring copies of your image in the spatial domain rotate, but also expand and come into you image.
The simplest way to avoid this is to pad the image with zeros to double its size.
import numpy as np
import cv2
from numpy.fft import fftshift as fftshift
from numpy.fft import ifftshift as ifftshift
angle = 30
M = cv2.imread("shepp-logan-small.tif")
M = cv2.cvtColor(M, cv2.COLOR_BGR2GRAY)
M = np.float32(M)
# Pad
v, h = M.shape
v //= 2
h //= 2
M = cv2.copyMakeBorder(M, v, v, h, h, cv2.BORDER_CONSTANT)
Now you can apply the rotation in the frequency domain like before. But note that the center of rotation should the pixel at shape//2, do not use a true division! Also note that we no longer need to apply a window function.
sM = fftshift(M)
rotation_center = (M.shape[1]//2,M.shape[0]//2)
rot_matrix = cv2.getRotationMatrix2D(rotation_center,angle,1.0)
FsM = fftshift(cv2.dft(sM,flags = cv2.DFT_COMPLEX_OUTPUT))
rFsM = cv2.warpAffine(FsM,rot_matrix,(FsM.shape[1],FsM.shape[0]),flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
IrFsM = ifftshift(cv2.idft(ifftshift(rFsM),flags=cv2.DFT_REAL_OUTPUT))
Finally, crop the result back to its original size.
# Crop
IrFsM = IrFsM[v:-v, h:-h]
Do note that the result is not pretty. It is much better to rotate in the spatial domain because, as you said, interpolation in the frequency domain is not really sensical.

Access Z coordinate in a LINESTRING Z in geopandas?

I have a GeoDataFrame with a LINESTRING Z geometry where Z is my altitude for the lat/long. (There are other columns in the dataframe that I deleted for ease of sharing but are relevant when displaying the resulting track)
TimeUTC Latitude Longitude AGL geometry
0 2021-06-16 00:34:04+00:00 42.835413 -70.919610 82.2 LINESTRING Z (-70.91961 42.83541 82.20000, -70...
I would like to find the maximum Z value in that linestring but I am unable to find a way to access it or extract the x,y,z values in a way that I can determine the maximum value outside of the linestring.
line.geometry.bounds only returns the x,y min/max.
The best solution I could come up with was to turn all the points into a list of tuples:
points = line.apply(lambda x: [y for y in x['geometry'].coords], axis=1)
And then find the maximum value of the third element:
from operator import itemgetter
max(ft2,key=itemgetter(2))[2]
I hope there is a better solution available.
Thank you.
You can take your lambda function approach and just take it one step further:
import numpy as np
line['geometry'].apply(lambda geom: np.max([coord[2] for coord in geom.coords]))
Here's a fully reproducible example from start to finish:
import shapely
import numpy as np
import geopandas as gpd
linestring = shapely.geometry.LineString([[0,0,0],
[1,1,1],
[2,2,2]])
gdf = gpd.GeoDataFrame({'id':[1,2,3],
'geometry':[linestring,
linestring,
linestring]})
gdf['max_z'] = (gdf['geometry']
.apply(lambda geom:
np.max([coord[2] for coord in geom.coords])))
In the example above, I create a new column called "max_z" that stores the maximum Z value for each row.
Important note
This solution will only work if you exclusively have LineStrings in your geometries. If, for example, you have MultiLineStrings, you'll have to adapt the function I wrote to take care of that.

The point that minimizes the sum of euclidean distances to a set of n points

I have a set of points W={(x1, y1), (x2, y2),..., (xn, yn)} on the 2D plane. Can you find an algorithm that takes these points as the input and returns a point (x, y) on the 2D plane which has the minimum sum of distances from the points in W? In other words, if
di = Euclidean_distance((x, y), (xi, yi))
I want to minimize:
d1 + d2 + ... + dn
The Problem
You're looking for the geometric median.
An Easy Solution
There is no closed-form solution to this problem, so iterative or probabilistic methods are used. The easiest way to find this is probably with Weiszfeld's algorithm:
We can implement this in Python as follows:
import numpy as np
from numpy.linalg import norm as npnorm
c_pt_old = np.random.rand(2)
c_pt_new = np.array([0,0])
while npnorm(c_pt_old-c_pt_new)>1e-6:
num = 0
denom = 0
for i in range(POINT_NUM):
dist = npnorm(c_pt_new-pts[i,:])
num += pts[i,:]/dist
denom += 1/dist
c_pt_old = c_pt_new
c_pt_new = num/denom
print(c_pt_new)
There's a chance that Weiszfeld's algorithm won't converge, so it might be best to run it several times from different starting points.
A General Solution
You can also find this using second-order cone programming (SOCP). In addition to solving your specific problem, this general formulation then allows you to easily add constraints and weightings, such as variable uncertainty in the location of each data point.
To do so, you create a number of indicator variables representing the distance between the proposed center point and the data points.
You then minimize the sum of the indicator variables. The result follows
import cvxpy as cp
import numpy as np
import matplotlib.pyplot as plt
#Generate random test data
POINT_NUM = 100
pts = np.random.rand(POINT_NUM,2)
c_pt = cp.Variable(2) #The center point we wish to locate
distances = cp.Variable(POINT_NUM) #Distance from the center point to each data point
#Generate constraints. These are used to hold distances.
constraints = []
for i in range(POINT_NUM):
constraints.append( cp.norm(c_pt-pts[i,:])<=distances[i] )
objective = cp.Minimize(cp.sum(distances))
problem = cp.Problem(objective,constraints)
optimal_value = problem.solve()
print("Optimal value = {0}".format(optimal_value))
print("Optimal location = {0}".format(c_pt.value))
plt.scatter(x=pts[:,0], y=pts[:,1], s=1)
plt.scatter(c_pt.value[0], c_pt.value[1], s=10)
plt.show()
SOCPs are available in a number of solvers including CPLEX, Elemental, ECOS, ECOS_BB, GUROBI, MOSEK, CVXOPT, and SCS.
I've tested and the two approaches give the same answers to within tolerance.
Weiszfeld, E. (1937). "Sur le point pour lequel la somme des distances de n points donnes est minimum". Tohoku Mathematical Journal. 43: 355–386.
If that point does not need to be from your sample, then the mean minimises the euclidean distance.
A third method would be to use a compact nonlinear programming formulation. An unconstrained NLP model would be:
min sum(i, ||x-p(i)|| )
This has just 2 variables (the coordinates of x).
There is a very good initial point available. Let p(i,c) be the coordinates of the data points. Then the mean is
m(c) = sum(i, p(i,c)) / n
where n is the number of data points. This point is often very close to the optimal value of x. So we can use m as an excellent initial point for x.
Some limited experiments indicate this approach is quite faster than a cone programming formulation for large n.
For details see Yet Another Math Programming Consultant - Finding the Central Point in a Point Cloud blog post.

Fitting of a sphere using SVD/LMS

I would like to fit a MR binary data of 281*398*104 matrix which is not a perfect sphere, and find out the center and radius of sphere and error also. I know LMS or SVD is a good choice to fit for sphere.
I have tried sphereFit from matlab file exchange but got an error,
>> sphereFit(data)
Warning: Matrix is singular to working precision.
> In sphereFit at 33
ans =
NaN NaN NaN
Would you let me know where is the problem, or any others solution?
If you want to use sphere fitting algorithm you should first extract the boundary points of the object you assume to be a sphere. The result should be represented by a N-by-3 array containing coordinates of the points. Then you can apply sphereFit function.
In order to obtain boundary point of a binary object, there are several methods. One method is to apply morphological erosion (you need the "imerode" function from the image processing toolbox) with small structuring element, then compute set difference between the two images, and finally use the "find" function to transform binary image into a coordinate array.
the idea is as follow:
dataIn = imerode(data, ones([3 3 3]));
bnd = data & ~data2;
inds = find(bnd);
[y, x, z] = ind2sub(size(data), inds); % be careful about x y order
points = [x y z];
sphere = sphereFitting(points);
By the way, the link you gave refers to circle fitting, I suppose you wanted to point to a sphere fitting submission?
regards,

Hole in a Polygon

I need to create a 3D model of a cube with a circular hole punched at the center of one face passing completely through the cube to the opposite side. I am able to generate the vertices for the faces and for the holes.
Four of the faces (untouched by the hole) can be modeled as a single triangle strip. The inside of the hole can also be modeled as a single triangle strip.
How do I generate the index buffer for the faces with the holes? Is there a standard algorithm(s) to do this?
I am using Direct3D but ideas from elsewhere are welcome.
To generate the index-buffer you want, you could do like this. Thinking in 2D with the face in question as a square with vertices (±1, ±1), and the hole as a circle in the middle.
You walk along the edge of the circle, dividing it into some number of segments.
For each vertex, you project it onto the surrounding square with (x/M,y/M), where M is max(abs(x),abs(y)). M is the absolute value of the biggest coordinate, so this will scale (x,y) so that the biggest coordinate is ±1.
This line you also divide into some number of segments.
The segments of two succeeding lines you join pairwise as faces.
This is an example, subdividing the circle into 64 segments, and each ray into 8 segments. You can choose the numbers to match your requirements.
alt text http://pici.se/pictures/AVhcssRRz.gif
Here is some Python code that demonstrates this:
from math import sin, cos, pi
from itertools import izip
def pairs(iterable):
"""Yields the previous and the current item on each iteration.
"""
last = None
for item in iterable:
if last is not None:
yield last, item
last = item
def circle(radius, subdiv):
"""Yields coordinates of a circle.
"""
for angle in xrange(0,subdiv+1):
x = radius * cos(angle * 2 * pi / subdiv)
y = radius * sin(angle * 2 * pi / subdiv)
yield x, y
def line(x0,y0,x1,y1,subdiv):
"""Yields coordinates of a line.
"""
for t in xrange(subdiv+1):
x = (subdiv - t)*x0 + t*x1
y = (subdiv - t)*y0 + t*y1
yield x/subdiv, y/subdiv
def tesselate_square_with_hole((x,y),(w,h), radius=0.5, subdiv_circle=64, subdiv_ray=8):
"""Yields quads of a tesselated square with a circluar hole.
"""
for (x0,y0),(x1,y1) in pairs(circle(radius,subdiv_circle)):
M0 = max(abs(x0),abs(y0))
xM0, yM0 = x0/M0, y0/M0
M1 = max(abs(x1),abs(y1))
xM1, yM1 = x1/M1, y1/M1
L1 = line(x0,y0,xM0,yM0,subdiv_ray)
L2 = line(x1,y1,xM1,yM1,subdiv_ray)
for ((xa,ya),(xb,yb)),((xc,yc),(xd,yd)) in pairs(izip(L1,L2)):
yield ((x+xa*w/2,y+ya*h/2),
(x+xb*w/2,y+yb*h/2),
(x+xc*w/2,y+yc*h/2),
(x+xd*w/2,y+yd*h/2))
import pygame
def main():
"""Simple pygame program that displays the tesselated figure.
"""
print('Calculating faces...')
faces = list(tesselate_square_with_hole((150,150),(200,200),0.5,64,8))
print('done')
pygame.init()
pygame.display.set_mode((300,300))
surf = pygame.display.get_surface()
running = True
while running:
need_repaint = False
for event in pygame.event.get() or [pygame.event.wait()]:
if event.type == pygame.QUIT:
running = False
elif event.type in (pygame.VIDEOEXPOSE, pygame.VIDEORESIZE):
need_repaint = True
if need_repaint:
print('Repaint')
surf.fill((255,255,255))
for pa,pb,pc,pd in faces:
# draw a single quad with corners (pa,pb,pd,pc)
pygame.draw.aalines(surf,(0,0,0),True,(pa,pb,pd,pc),1)
pygame.display.flip()
try:
main()
finally:
pygame.quit()
You want to look up Tessellation which is the area of math that deals with what MizardX is showing.Folks in 3D Graphcs have to deal with this all the time and there are a variety of tessellation algorithms to take a face with a hole and calculate the triangles needed to render it.
Modern hardware usually can't render concave polygons correctly.
Specifically, there usually isn't even a way to define a polygon with a hole.
You'll need to find a triangulation of the plane around the hole somehow. The best way is probably to create triangles from a vertex of the hole to the nearest vertices of the rectangular face. This will probably create some very thin triangles. if that's not a problem then you're done. if it is then you'll need some mesh fairing/optimization algorithm to create nice looking triangles.
Is alpha blending out of the question? If not, just texture the sides with holes using a texture that has transparency in the middle. You have to do more rendering of polygons since you can't take advantage of drawing front-to-back and ignoring covered faces, but it might be faster than having a lot tiny triangles.
I'm imagining 4 triangle fans coming from the 4 corners of the square.
Just a thought -
If you're into cheating (as done many times in games), you can always construct a regular cube but have the texture for the two faces you desire with a hole (alpha = 0), you can then either clip it in the shader, or blend it (in which case you need to render with Z sort).
You get the inside hole by constructing an inner cylinder facing inwards and with no caps.
Of course this will only work if the geometry is not important to you.

Resources