Trigonometry to find landscape visibility - python

I'm trying to find the degrees of visibility of natural environments from a number of viewpoints. I have a DEM, and a raster defining the natural environments. For each cell I have slope, height, aspect and distance from viewpoint. I want to calculate the angle between the viewpoint and each cell.
I originally did this by getting the x,y,z coordinates for the viewpoint and the both the bottom and top of a sloped cell. Then get the 3D distance between each coordinate and use these distances to get the angles. I would then sum for each cell to get total degrees of visibilty.
Problem is that this approach only works when the bearing between the viewpoint and the visible cell is in line with the x and y axis of the coordinate system. This because to calculate the points coordinates for the cell corners I'm subtracting and adding the cell resolution/2 to the cell centroid.
Code below
def Cell_Loc (VPX, VPY, VPZ, cell_x, cell_y, cell_z, aspect, cell_resolution, bearing):
import math
from math import sqrt
#Get change in height of cell using slope and cell resolution (trig)
AspectTan = math.tan(math.radians(aspect))
Opposite = (AspectTan * cell_res)
#Get coordinates for cell corners
CloseCornerX = cent_x - 2.5
CloseCornerY = cent_y - 2.5
FarCornerX = cent_x + 2.5
FarCornerY = cent_y + 2.5
CloseCornerZ = cent_z - (Opposite/2)
FarCornerZ = cent_z + (Opposite/2)
#Get Distance between coordinates
VP = (VPX, VPY, VPZ) #data point 1
cellcrner1 = (CloseCornerX, CloseCornerY, CloseCornerZ) #data point 2
cellcrner2 = (FarCornerX, FarCornerY, FarCornerZ)
dist_to_far_corner = sqrt(sum( (VP - cellcrner2)**2 for VP, cellcrner2 in zip(VP, cellcrner2)))
dist_to_close_corner = sqrt(sum( (VP - cellcrner1)**2 for VP, cellcrner1 in zip(VP, cellcrner1)))
cell_dist = sqrt(sum( (cellcrner1 - cellcrner2)**2 for cellcrner1, cellcrner2 in zip(cellcrner1, cellcrner2)))
#Input above distances into ViewAngle to find angle
def ViewAngle (a, b, c):
"calculates the viewing angle of each visible cell"
import math
a2 = a*a
b2 = b*b
c2 = c*c
VA = (a2-b2+c2)/ (2.0 * (a*c))
rads = math.acos(VA)
return math.degrees(rads)
How can I improve this code, or maybe another approach is a better way of doing it. The output I need is to say "for this viewpoint, there are X amount of degrees for visible natural environments". Again I have, DEM, visible areas from viewshed analysis, slope, aspect and distances between viewpoint and cell centroid. I use python programming language and have ArcGIS10.2 with arcpy

One improvement would be to use coordinates and inner products instead of distances to calculate angles: this would help avoid some (though not all) expensive and messy square roots. So if your view point is at v=(x,y,z), and two other points are P=(a,b,c) and Q=(d,e,f), the subtended angle is acos(sqrt(((V-P)(V-Q))^2/(V-P)^2(V-Q)^2))=acos(sqrt(<(V-P),(V-Q)>^2/<(V-P),(V-P)><(V-Q),(V-Q)>)) where the (internal) products are inner products, i.e., say PQ = ad + be + cf. Also, some of the products and inner products can be 'recycled' instead of recalculating.

Related

Procedurally generating areas with distinctly higher elevations with Perlin Noise

I am trying to learn about Perlin Noise and procedural generation. I am reading through an online tutorial about generating landscapes with noise, but I don't understand part of the author's explanation about making areas with higher elevation.
On this webpage under the "islands" section there is the text
Design a shape that matches what you want from islands. Use the lower shape to push the map up and the upper shape to push the map down. These shapes are functions from distance d to elevation 0-1. Set e = lower(d) + e * (upper(d) - lower(d)).
I want to do this, but I'm not sure what the author means when they're talking about upper and lower shapes.
What could the author mean by "Use the lower shape to push the map up and the upper shape to push the map down"?
Code Example:
from __future__ import division
import numpy as np
import math
import noise
def __noise(noise_x, noise_y, octaves=1, persistence=0.5, lacunarity=2):
"""
Generates and returns a noise value.
:param noise_x: The noise value of x
:param noise_y: The noise value of y
:return: numpy.float32
"""
value = noise.pnoise2(noise_x, noise_y,
octaves, persistence, lacunarity)
return np.float32(value)
def __elevation_map():
elevation_map = np.zeros([900, 1600], np.float32)
for y in range(900):
for x in range(1600):
noise_x = x / 1600 - 0.5
noise_y = y / 900 - 0.5
# find distance from center of map
distance = math.sqrt((x - 800)**2 + (y - 450)**2)
distance = distance / 450
value = __noise(noise_x, noise_y, 8, 0.9, 2)
value = (1 + value - distance) / 2
elevation_map[y][x] = value
return elevation_map
The author means that you should describe the final elevation, fe, of a point in terms of its distance from the centre, d, as well as the initial elevation, e, which was presumably generated by noise.
So, for example, if you wanted your map to look something like a bowl, but maintaining the noisy characteristic of your originally generated terrain, you could use the following functions:
def lower(d):
# the lower elevation is 0 no matter how near you are to the centre
return 0
def upper(d):
# the upper elevation varies quadratically with distance from the centre
return d ** 2
def modify(d, initial_e):
return lower(d) + initial_e * (upper(d) - lower(d))
Note in particular the paragraph starting with "How does this work?", which I found quite illuminating.

Particle-particle collision in box with periodic boundaries

The Task
For a class in molecular dynamics, I have to simulate 100 particles in a box with periodic boundaries. I have to take particle-particle collisions into account, since the walls are 'transparent' those interactions can be happen across the boundaries. Since the simulation should cover 50000 steps, and I'm expecting more and more additional tasks in the future, I want my code as efficient as possible (I have to use python, despite the long run time).
The Setting
The system consists of
100 Particles
Box with x = 10, y = 5
Mass = 2
Radius = 0.2
Velocity |v| = 0.5 per step
Simulation of 50000 steps
What I've done so fare
I have found this example for particles in a box with particle-particle collision. Since the author used a efficient implementation, I took his approach.
My relevant code parts are (in strong resemblance to the linked site):
class particlesInPeriodicBox:
# define the particle properties
def __init__(self,
initialState = [[0,0,0,0]], # state with form [x, y, vx, vy] for each particle
boundaries = [0, 10, 0, 5], # box boundaries with form [xmin, xmax, ymin, ymax] in nm
radius = 0.2, # particle radius in nm
mass = 2): # mass in g/mol. Here a parameter instead of a global variable
self.initialState = np.asarray(initialState, dtype=float)
self.state = self.initialState.copy()
self.radius = radius
self.time = 0 # keep count of time, if time, i want to implement a 'clock'
self.mass = mass # mass
self.boundaries = boundaries
def collision(self):
"""
now, one has to check for collisions. I do this by distance check and will solve this problems below.
To minimize the simulation time I then will only consider the particles that colided.
"""
dist = squareform(pdist(self.state[:, :2])) # direct distance
colPart1, colPart2 = np.where(dist < 2 * self.radius) # define collision partners 1 and 2 as those where the distance between the centeres are smaller than two times the radius
# resolve self-self collissions
unique = (colPart1 < colPart2)
colPart1 = colPart1[unique]
colPart2 = colPart2[unique]
"""
The following loop resolves the collisions. I zip the lists of collisionpartners to one aray,
where one entry contains both colisionpartners.
"""
for cp1, cp2 in zip(colPart1, colPart2): # cp1/cp2 are the two particles colliding in one collision.
# masses could be different in future...
m1 = self.mass[cp1]
m2 = self.mass[cp2]
# take the position (x,y) tuples for the two particles
r1 = self.state[cp1, :2]
r2 = self.state[cp2, :2]
# same with velocities
v1 = self.state[cp1, 2:]
v2 = self.state[cp2, 2:]
# get relative parameters
r = r1-r2
v = r2-r1
# center of mass velocity:
vcm = (m1 * v1 + m2 * v2) / (m1 + m2)
"""
This is the part with the elastic collision
"""
dotrr = np.dot(r, r) # the dot product of the relative position with itself
dotvr = np.dot(v, r) # the dot product of the relative velocity with the relative position
v = 2 * r * dotvr / dotrr - v # new relative velocity
"""
In this center of mass frame, the velocities 'reflect' on the center of mass in oposite directions
"""
self.state[cp1, 2:] = vcm + v * m2/(m1 + m2) # new velocity of particle 1 still considering possible different masses
self.state[cp2, 2:] = vcm - v * m1/(m1 + m2) # new velocity of particle 2
As I understand it, this technique of handling the operations to the whole arrays is more efficient than manually looping through it every time. Moving the particles 'trough' the wall is easy, I just subtract or add the dimension of the box, respectively. But:
The Problem
For now the algorithm only sees collision inside the box, but not across the boundaries. I thought about this problem a while now, and come up with the following Ideas:
I could make a total of 9 copies of this system in a 3x3 grid, and only considering the middle one, can so look into the neighboring cells for the nearest neighbor search. BUT i can't think of a effective way to implement this despite the fact, that this approach seams to be the standard way
Every other idea has some hand waving use of modulo, and im almost sure, that this is not the way to go.
If I had to boil it down, I guess my key questions are:
How do I take periodic boundaries into account when calculating
the distance between particles?
the actual particle-particle collision (elastic) and resulting directions?
For the first problem it might be possible to use techniques like in Calculation of Contact/Coordination number with Periodic Boundary Conditions, but Im not sure if that is the most efficient way.
Thank you!
Modulus is likely as quick an operation as you're going to get. In any self-respecting run-time system, this will attach directly to the on-chip floating-divide operations, which may well be faster than a tedious set of "if-subtract" pairs.
I think your 9-cell solution is overkill. Use 4 cells in a 2x2 matrix and check two regions: the original cell, and the same dimensions centered on the "four corners" point (middle of the 2x2). For any pair of points, the proper distance is the lesser of these two. Note that this method also gives you a frame in which you can easily calculate the momentum changes.
A third possible approach is to double the dimensions (ala the 2x2 above), but give each particle four sets of coordinates, one in each box. Alter your algorithms to consider all four when computing distance. If you have good vectorization packages and parallelism, this might be the preferred solution.

Is there an algorithm to calculate the area of a Lissajous figure?

Suppose I have measurements of two signals
V = V(t) and U = U(t)
that are periodic in time with a phase difference between them. When plotted against each other in a graph V vs U they form a Lissajous figure, and I want to calculate the area inside it.
Is there an algorithm for such calculation?
I would like to solve this problem using Python. But a response in any language or an algorithm to do it will be very appreciated.
Examples of V and U signals can be generated using expressions like:
V(t) = V0*sin(2*pi*t) ; U(t) = U0*sin(2*pi*t + delta)
Figure 1 shows a graph of V,U vs t for V0=10, U0=5, t=np.arange(0.0,2.0,0.01) and delta = pi/5.
And Figure 2 shows the corresponding Lissajous figure V vs U.
This is an specific problem of a more general question: How to calculate a closed path integral obtained with a discrete (x_i,y_i) data set?
To find area of (closed) parametric curve in Cartesian coordinates, you can use Green's theorem (4-th formula here)
A = 1/2 * Abs(Integral[t=0..t=period] {(V(t) * U'(t) - V'(t) * U(t))dt})
But remember that interpretation - what is real area under self-intersected curves - is ambiguous, as #algrid noticed in comments
for the outer most curves area of usual Lissajous shapes I would try this:
find period of signal
so find T such:
U(t) = U(t+T)
V(t) = V(t+T)
sample data on t=<0,T>
I would use polar coordinate system with center equal to average U,V coordinate on interval t=<0,T> and call it U0,V0. Convert and store the data in polar coordinates so:
a(t)=atan2( V(t)-V0 , U(t)-U0 )
r(t)=sqrt( (U(t)-U0)^2 + (V(t)-V0)^2 )
and remember only the points with max radius for each angle position. That can be done either with arrays (limiting precision in angle) or geometricaly by computing polyline intersection with overlapping segments. and removing inside parts.
Compute the area from sampled data
So compute the the area by summing the pie triangles for each angular position covering whole circle.
This may not work for exotic shapes.
Both solutions above - by #MBo and by #Spektre (and #meowgoesthedog in the comments) - works fine. Thank you guys.
But I found another way to calculate the area A of an elliptical Lissajous curve: use the A = Pi*a*b formula (a and b are, respectively, the major and minor semi axis of the ellipse).
Steps:
1 - Find the period T of the V (or U) signal;
2 - In the time interval 0<t<T:
2.a - calculate the average values of V and U (V0 and U0), in order to determine the center of the ellipse;
2.b - calculate the distance r(t) from the point (V0,U0) using:
r(t)=sqrt( (U(t)-U0)^2 + (V(t)-V0)^2 )
3 - Find a and b values using:
a = max(r(t)); b = min(r(t))
4 - calculate A: A = Pi*a*b
The Lissajous curves will always be elliptical if the U,V signals are sinusoidal-like and have the same frequency.
Seizing the opportunity, I will propose a solution for the case where the V,U signals are triangular and have the same frequency. In this case, the Lissajous curve will be a parallelogram, then one can calculate its area A using A = 2*|D|*|d|*sin(q), where |D| and |d| are, respectively, the length of major and minor semi diagonals of the parallelogram and q is the angle between the vectors D and d.
Repeat steps 1 and 2 for the elliptical case.
In step 3 we will have:
|D| = max(r(t)) = r(t1); |d| = min(r(t)) = r(t2)
4' - Obtain t1 and t2 and use them to get the coordinates (V(t1)=V1,U(t1)=U1) and (V(t2)=V2,U(t2)=U2). Then the vectors D and d can be written as:
D=(V1,U1)-(V0,U0); d=(V2,U2)-(V0,U0)
5' - Calculate the angle q between D and d;
6' - Perform the calculation of A: A = 2*|D|*|d|*sin(q)

Can't reproduce distance value between sources obtained with astropy

I have two sources with equatorial coordinates (ra, dec) and (ra_0, dec_0) located at distances r and r_0, and I need to calculate the 3D distance between them.
I use two approaches that should give the same result as far as I understand, but do not.
The first approach is to apply astropy's separation_3d function. The second approach is to use the expression that gives the distance between two sources with spherical coordinates:
as shown here.
In the MCVE below, the values returned are:
91.3427173002 pc
93.8470493776 pc
Shouldn't these two values be equal?
MCVE:
from astropy.coordinates import SkyCoord
from astropy import units as u
import numpy as np
# Define some coordinates and distances for the sources.
c1 = SkyCoord(ra=9.7*u.degree, dec=-50.6*u.degree, distance=1500.3*u.pc)
c2 = SkyCoord(ra=7.5*u.degree, dec=-47.6*u.degree, distance=1470.2*u.pc)
# Obtain astropy's distance between c1 & c2 coords.
print c1.separation_3d(c2)
# Obtain distance between c1 & c2 coords using explicit expression.
ra_0, dec_0, r_0 = c1.ra.radian, c1.dec.radian, c1.distance
ra, dec, r = c2.ra.radian, c2.dec.radian, c2.distance
alpha_delta_par = np.sin(dec) * np.sin(dec_0) * np.cos(ra - ra_0) +\
np.cos(dec) * np.cos(dec_0)
d_pc = np.sqrt(r**2 + r_0**2 - 2*r*r_0*alpha_delta_par)
print d_pc
This is a problem with coordinate systems, and the difference between declination (astral coordinates) and polar angle θ (spherical coordinates) :-)
Astral coordinates define declination as north of the celestial equator, while spherical coordinates define polar angle θ as downward from from vertical.
If you change your alpha_delta_par to account for this 90° difference by adding np.pi/2 to all your declination terms, you get
alpha_delta_par = np.sin(np.pi/2 + dec)*np.sin(np.pi/2 + dec0)*np.cos(ra - ra0) +\
np.cos(np.pi/2 + dec)*np.cos(np.pi/2 + dec0)
Which gives the correct result: 91.3427173002 pc.
Turns out physicists usually use the symbol θ as the polar angle and mathemeticians usually use φ; I went with θ because I followed my heart. I'm not making this up I swear.

Generate random points on a surface of the cylinder

I want to generate random points on the surface of cylinder such that distance between the points fall in a range of 230 and 250. I used the following code to generate random points on surface of cylinder:
import random,math
H=300
R=20
s=random.random()
#theta = random.random()*2*math.pi
for i in range(0,300):
theta = random.random()*2*math.pi
z = random.random()*H
r=math.sqrt(s)*R
x=r*math.cos(theta)
y=r*math.sin(theta)
z=z
print 'C' , x,y,z
How can I generate random points such that they fall with in the range(on the surfaceof cylinder)?
This is not a complete solution, but an insight that should help. If you "unroll" the surface of the cylinder into a rectangle of width w=2*pi*r and height h, the task of finding distance between points is simplified. You have not explained how to measure "distance along the surface" between points on the top of the cylinder and the side- this is a slightly tricky bit of geometry.
As for computing the distance along the surface when we created an artificial "seam", just use both (x1-x2) and (w -x1+x2) - whichever gives the shorter distance is the one you want.
I do think that #VincentNivoliers' suggestion to use Poisson disk sampling is very good, but with the constraints of h=300 and r=20 you will get terrible results no matter what.
The basic way of creating a set of random points with constraints in the positions between them, is to have a function that modulates the probability of points being placed at a certain location. this function starts out being a constant, and whenever a point is placed, forbidden areas surrounding the point are set to zero. That is difficult to do with continuous variables, but reasonably easy if you discretize your problem.
The other thing to be careful about is the being on a cylinder part. It may be easier to think of it as random points on a rectangular area that repeats periodically. This can be handled in two different ways:
the simplest is to take into consideration not only the rectangular tile where you are placing the points, but also its neighbouring ones. Whenever you place a point in your main tile, you also place one in the neighboring ones and compute their effect on the probability function inside your tile.
A more sophisticated approach considers the probability function then convolution of a kernel that encodes forbidden areas, with a sum of delta functions, corresponding to the points already placed. If this is computed using FFTs, the periodicity is anatural by product.
The first approach can be coded as follows:
from __future__ import division
import numpy as np
r, h = 20, 300
w = 2*np.pi*r
int_w = int(np.rint(w))
mult = 10
pdf = np.ones((h*mult, int_w*mult), np.bool)
points = []
min_d, max_d = 230, 250
available_locs = pdf.sum()
while available_locs:
new_idx = np.random.randint(available_locs)
new_idx = np.nonzero(pdf.ravel())[0][new_idx]
new_point = np.array(np.unravel_index(new_idx, pdf.shape))
points += [new_point]
min_mask = np.ones_like(pdf)
if max_d is not None:
max_mask = np.zeros_like(pdf)
else:
max_mask = True
for p in [new_point - [0, int_w*mult], new_point +[0, int_w*mult],
new_point]:
rows = ((np.arange(pdf.shape[0]) - p[0]) / mult)**2
cols = ((np.arange(pdf.shape[1]) - p[1]) * 2*np.pi*r/int_w/mult)**2
dist2 = rows[:, None] + cols[None, :]
min_mask &= dist2 > min_d*min_d
if max_d is not None:
max_mask |= dist2 < max_d*max_d
pdf &= min_mask & max_mask
available_locs = pdf.sum()
points = np.array(points) / [mult, mult*int_w/(2*np.pi*r)]
If you run it with your values, the output is usually just one or two points, as the large minimum distance forbids all others. but if you run it with more reasonable values, e.g.
min_d, max_d = 50, 200
Here's how the probability function looks after placing each of the first 5 points:
Note that the points are returned as pairs of coordinates, the first being the height, the second the distance along the cylinder's circumference.

Categories