Triangle function on angles - python-2.7

I need a function that works on angles (degrees for the example, but it may be radians as well) with the following (it must be continuous, I only write down some key values to understand the behavior):
any_function(angle):
0-> 0.0
45-> 0.5
90-> 1.0
135-> 0.5
180-> 0.0
225->-0.5
270->-1.0
335->-0.5
360-> 0.0
This is a simple triangular wave.
Does anything already exist in some modules or do I need to create it on my own?
For example like this (quick and dirty):
def toTriangle(angle):
if angle < 90 and angle >= 0:
t = (angle/90.)
elif angle <= 180 and angle >= 90:
t = 2-(angle/90.)
elif angle <= 270 and angle >= 180:
t = -(3-(angle/90.))
else:
t = -(4-(angle/90.))
return t

Thanks DeepSpace for your suggestion but it doesn't work out of the box:
from scipy.signal import sawtooth
sawtooth(0)
Out[4]: array(-1.0)
Here's a less quick-n-dirty solution:
def toTriangle(angle):
import math
tr = 1-4*math.fabs(0.5-math.modf(0.25+0.5*angle%(2*math.pi)/math.pi)[0] )
return t
( http://mathworld.wolfram.com/TriangleWave.html )

Related

how to make complex shapes using swarm of dots......like chair,rocket and many more using pygame and numpy

i am working on a project of swarm algorithms and i am trying to make complex shapes using the swarm consensus. However, the mathematics to achieve that seems quite difficult for me.
I have been able to make shapes like stars, circle and triangle but to make other complex shapes seems more harder. It would be very helpful if i get the idea of using numpy arrays to build these complex shapes using swarms....................................................
# general function to reset radian angle to [-pi, pi)
def reset_radian(radian):
while radian >= math.pi:
radian = radian - 2*math.pi
while radian < -math.pi:
radian = radian + 2*math.pi
return radian
# general function to calculate next position node along a heading direction
def cal_next_node(node_poses, index_curr, heading_angle, rep_times):
for _ in range(rep_times):
index_next = index_curr + 1
x = node_poses[index_curr][0] + 1.0*math.cos(heading_angle)
y = node_poses[index_curr][1] + 1.0*math.sin(heading_angle)
node_poses[index_next] = np.array([x,y])
index_curr = index_next
return index_next
##### script to generate star #####
filename = 'star'
swarm_size = 30
node_poses = np.zeros((swarm_size, 2))
outer_angle = 2*math.pi / 5.0
devia_right = outer_angle
devia_left = 2*outer_angle
# first node is at bottom left corner
heading_angle = outer_angle / 2.0 # current heading
heading_dir = 0 # current heading direction: 0 for left, 1 for right
seg_count = 0 # current segment count
for i in range(1,swarm_size):
node_poses[i] = (node_poses[i-1] +
np.array([math.cos(heading_angle), math.sin(heading_angle)]))
seg_count = seg_count + 1
if seg_count == 3:
seg_count = 0
if heading_dir == 0:
heading_angle = reset_radian(heading_angle - devia_right)
heading_dir = 1
else:
heading_angle = reset_radian(heading_angle + devia_left)
heading_dir = 0
print(node_poses)
with open(filename, 'w') as f:
pickle.dump(node_poses, f)
pygame.init()
# find the right world and screen sizes
x_max, y_max = np.max(node_poses, axis=0)
x_min, y_min = np.min(node_poses, axis=0)
pixel_per_length = 30
world_size = (x_max - x_min + 2.0, y_max - y_min + 2.0)
screen_size = (int(world_size[0])*pixel_per_length, int(world_size[1])*pixel_per_length)
# convert node poses in the world to disp poses on screen
def cal_disp_poses():
poses_temp = np.zeros((swarm_size, 2))
# shift the loop to the middle of the world
middle = np.array([(x_max+x_min)/2.0, (y_max+y_min)/2.0])
for i in range(swarm_size):
poses_temp[i] = (node_poses[i] - middle +
np.array([world_size[0]/2.0, world_size[1]/2.0]))
# convert to display coordinates
poses_temp[:,0] = poses_temp[:,0] / world_size[0]
poses_temp[:,0] = poses_temp[:,0] * screen_size[0]
poses_temp[:,1] = poses_temp[:,1] / world_size[1]
poses_temp[:,1] = 1.0 - poses_temp[:,1]
poses_temp[:,1] = poses_temp[:,1] * screen_size[1]
return poses_temp.astype(int)
disp_poses = cal_disp_poses()
# draw the loop shape on pygame window
color_white = (255,255,255)
color_black = (0,0,0)
screen = pygame.display.set_mode(screen_size)
screen.fill(color_white)
for i in range(swarm_size):
pygame.draw.circle(screen, color_black, disp_poses[i], 5, 0)
for i in range(swarm_size-1):
pygame.draw.line(screen, color_black, disp_poses[i], disp_poses[i+1],2)
pygame.draw.line(screen, color_black, disp_poses[0], disp_poses[swarm_size-1], 2)
pygame.display.update()
Your method for drawing takes huge advantage of the symmetries in the shapes you are drawing. More complex shapes will have fewer symmetries and so your method will require a lot of tedious work to get them drawn with stars. Without symmetry you may be better served writing each individual line 'command' in a list and following that list. For example, drawing the number 4 starting from the bottom (assuming 0 degrees is --> that way):
angles = [90,225,0]
distances = [20,15,12]
Then with a similar program to what you have, you can start drawing dots in a line at 90 degrees for 20 dots, then 225 degrees for 15 dots etc... Then by adding to these two lists you can build up a very complicated shape without relying on symmetry.

Finding negative numbers in Python CSV file

I have a CSV file I'm reading with Northing and Easting values I'm trying to calculate the direction angle they are facing in... have things mostly sorted out(I think) at this point, but the final values are all coming up between 0 and 90 it seems even though I have the following code in there...
if (u_eastward >= 0) and (v_northward >= 0):
azimouth = azimouth
elif (u_eastward >= 0 and v_northward <= 0):
azimouth = 180 - azimouth
elif (u_eastward <= 0 and v_northward <= 0):
azimouth = azimouth + 180
elif (u_eastward <= 0 and v_northward >= 0):
azimouth = 360 - azimouth
Azimouth is being calculated in Excel as '=ATAN(D2/C2)' to find radians and then being recalculated to the angle by '=DEGREES(E2)' on the arc tangent value which I think is correct.
Would appreciate any help.
Wasn't properly converting my string to a float so everything was being seen as a positive value.

Python boids implementation [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to implement a fairly basic boids simulation in python. My goal is to have a simulation with a basic predator prey setup. I found some pseudocode (can't post more than two links but it is the first result if you google boids pseudocode) and some code here and decided to give it a go. Because I wanted to add predators, I decided to give take the code I found modify it so that the boids (that will become prey) are sprites, and then go from there. However, I have run into this problem.
After I modified the code to use pygame sprites, all of the boids move to the lower right hand corner (the original code worked correctly).
My code (just clone the repo) is here(github). Has anyone ever run into the first issue? Does anyone have any ideas to solve it? As for question 2, could someone please explain how to do that?
Thank you and any help would be greatly appreciated.
P.S.
The behavior of the boids (their movement) appears to be working fine apart from the fact that they always go to the lower right hand corner.
P.P.S.
Thanks to furas the prey behave correctly now.
P.P.P.S.
As the debugging problem has been solved, the part of my question that remains involves an explanation, and I think should be on topic.
You have to differences in your code
You use different velocity speed at start - in __init__ but this shouldn't do difference.
You updates object in different moment.
You move all preys (using Group update) at the same time - after all calculations.
Original code moves every boid after its calculations so next boid use different data to calculate its move.
I put prey.update() inside for loops and remove all_sprites_list.update()
I organize code in a little different:
#!/usr/bin/env python
# Prey implementation in Python using PyGame
from __future__ import division # required in Python 2.7
import sys
import pygame
import random
import math
# === constants === (UPPER_CASE names)
SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
BLACK = (0, 0, 0)
MAX_VELOCITY = 10
NUM_BOIDS = 50
BORDER = 25
# === classes === (CamelCase names for classes / lower_case names for method)
class Prey(pygame.sprite.Sprite):
def __init__(self, x, y):
super(Prey, self).__init__()
# Load image as sprite
self.image = pygame.image.load("ressources/img/prey.png").convert()
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Coordinates
self.rect.x = x
self.rect.y = y
# self.velocityX = random.randint(-10, 10) / 10.0
# self.velocityY = random.randint(-10, 10) / 10.0
self.velocityX = random.randint(1, 10) / 10.0
self.velocityY = random.randint(1, 10) / 10.0
def distance(self, prey):
'''Return the distance from another prey'''
distX = self.rect.x - prey.rect.x
distY = self.rect.y - prey.rect.y
return math.sqrt(distX * distX + distY * distY)
def move_closer(self, prey_list):
'''Move closer to a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average distances from the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
if prey.rect.x == self.rect.x and prey.rect.y == self.rect.y:
continue
avgX += (self.rect.x - prey.rect.x)
avgY += (self.rect.y - prey.rect.y)
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
distance = math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0
self.velocityX -= (avgX / 100)
self.velocityY -= (avgY / 100)
def move_with(self, prey_list):
'''Move with a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average velocities of the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
avgX += prey.velocityX
avgY += prey.velocityY
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
self.velocityX += (avgX / 40)
self.velocityY += (avgY / 40)
def move_away(self, prey_list, minDistance):
'''Move away from a set of prey_list. This avoids crowding'''
if len(prey_list) < 1:
return
distanceX = 0
distanceY = 0
numClose = 0
for prey in prey_list:
distance = self.distance(prey)
if distance < minDistance:
numClose += 1
xdiff = (self.rect.x - prey.rect.x)
ydiff = (self.rect.y - prey.rect.y)
if xdiff >= 0:
xdiff = math.sqrt(minDistance) - xdiff
elif xdiff < 0:
xdiff = -math.sqrt(minDistance) - xdiff
if ydiff >= 0:
ydiff = math.sqrt(minDistance) - ydiff
elif ydiff < 0:
ydiff = -math.sqrt(minDistance) - ydiff
distanceX += xdiff
distanceY += ydiff
if numClose == 0:
return
self.velocityX -= distanceX / 5
self.velocityY -= distanceY / 5
def update(self):
'''Perform actual movement based on our velocity'''
if abs(self.velocityX) > MAX_VELOCITY or abs(self.velocityY) > MAX_VELOCITY:
scaleFactor = MAX_VELOCITY / max(abs(self.velocityX), abs(self.velocityY))
self.velocityX *= scaleFactor
self.velocityY *= scaleFactor
self.rect.x += self.velocityX
self.rect.y += self.velocityY
# === main === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
#screen_rect = screen.get_rect()
# --- objects ---
# lists
# This is a list of 'sprites.' Each block in the program is
# added to this list. The list is managed by a class called 'Group.'
prey_list = pygame.sprite.Group()
# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
# create prey_list at random positions
for i in range(NUM_BOIDS):
prey = Prey(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
# Add the prey to the list of objects
prey_list.add(prey)
all_sprites_list.add(prey)
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- updates ---
for prey in prey_list:
closeBoids = []
for otherBoid in prey_list:
if otherBoid == prey:
continue
distance = prey.distance(otherBoid)
if distance < 200:
closeBoids.append(otherBoid)
prey.move_closer(closeBoids)
prey.move_with(closeBoids)
prey.move_away(closeBoids, 20)
# ensure they stay within the screen space
# if we roubound we can lose some of our velocity
if prey.rect.x < BORDER and prey.velocityX < 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.x > SCREEN_WIDTH - BORDER and prey.velocityX > 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.y < BORDER and prey.velocityY < 0:
prey.velocityY = -prey.velocityY * random.random()
if prey.rect.y > SCREEN_HEIGHT - BORDER and prey.velocityY > 0:
prey.velocityY = -prey.velocityY * random.random()
prey.update()
# Calls update() method on every sprite in the list
#all_sprites_list.update()
# --- draws ---
screen.fill(BLACK)
# Draw all the spites
all_sprites_list.draw(screen)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
# Used to manage how fast the screen updates
clock.tick(120)
# --- the end ---
pygame.quit()
sys.exit()
EDIT: real problem was dividing of two integer numbers in Python 2 which gives result rounded to integer number
Solution:
from __future__ import division
Use this before other imports.

Counting the point which intercept in a line with opencv python

I am working in vehicle counting with opencv and python programming, I already complete step:
1. Detect moving vehicle with BackgroundSubtractorMOG2
2. Draw rectangle on it, then poin a centroid of that
3. Draw a line (to indicate of the counting)
if that centroid accros/intercept with the line I want count that 1. but in my code sometime it add sometime no. Here the line code:
cv2.line(frame,(0,170),(300,170),(200,200,0),2)
and there the centroid:
if w > 20 and h > 25:
cv2.rectangle(frame, (x,y), (x+w,y+h), (180, 1, 0), 1)
x1=w/2
y1=h/2
cx=x+x1
cy=y+y1
centroid=(cx,cy)
cv2.circle(frame,(int(cx),int(cy)),4,(0,255,0),-1)
my counting code:
if cy==170:
counter=counter+1
Can anyone help me. please. for your advice thankyou!
Here is my approach that would work independently of the video frame rate. Assuming that you are able to track a car's centroid at each frame, I would save the last two centroids' position (last_centroid and centroid in my code) and process as follows:
compute the intercepting line equation's parameters ( (a,b,c) from aX + bY + c = 0)
compute the equation's parameters of the segment line between last_centroid and centroid
find if the two lines are intersecting
if so, increment your counter
Here is how I implemented it in OpenCV (Python):
import cv2
import numpy as np
import collections
Params = collections.namedtuple('Params', ['a','b','c']) #to store equation of a line
def calcParams(point1, point2): #line's equation Params computation
if point2[1] - point1[1] == 0:
a = 0
b = -1.0
elif point2[0] - point1[0] == 0:
a = -1.0
b = 0
else:
a = (point2[1] - point1[1]) / (point2[0] - point1[0])
b = -1.0
c = (-a * point1[0]) - b * point1[1]
return Params(a,b,c)
def areLinesIntersecting(params1, params2, point1, point2):
det = params1.a * params2.b - params2.a * params1.b
if det == 0:
return False #lines are parallel
else:
x = (params2.b * -params1.c - params1.b * -params2.c)/det
y = (params1.a * -params2.c - params2.a * -params1.c)/det
if x <= max(point1[0],point2[0]) and x >= min(point1[0],point2[0]) and y <= max(point1[1],point2[1]) and y >= min(point1[1],point2[1]):
print("intersecting in:", x,y)
cv2.circle(frame,(int(x),int(y)),4,(0,0,255), -1) #intersecting point
return True #lines are intersecting inside the line segment
else:
return False #lines are intersecting but outside of the line segment
cv2.namedWindow('frame')
frame = np.zeros((240,320,3), np.uint8)
last_centroid = (200,200) #centroid of a car at t-1
centroid = (210,180) #centroid of a car at t
line_params = calcParams(last_centroid, centroid)
intercept_line_params = calcParams((0,170), (300,170))
print("Params:", line_params.a,line_params.b,line_params.c)
while(1):
cv2.circle(frame,last_centroid,4,(0,255,0), -1) #last_centroid
cv2.circle(frame,centroid,4,(0,255,0), -1) #current centroid
cv2.line(frame,last_centroid,centroid,(0,0,255),1) #segment line between car centroid at t-1 and t
cv2.line(frame,(0,170),(300,170),(200,200,0),2) #intercepting line
print("AreLinesIntersecting: ",areLinesIntersecting(intercept_line_params,line_params,last_centroid,centroid))
cv2.imshow('frame',frame)
if cv2.waitKey(15) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
And here are some results:
Fig1. Segment is intersecting the line (intercepting line in blue - segment line between last_centroid and centroid in red)
Fig2. Segment is NOT intersecting the line
N.B. I found the formulas to calculate the intersection point here.
I hope my approach will help to address your problem.
To assume that the centroid will assume a position 170 (in x or y) is wrong, because videos generally works at 30 fps, that mean you will get 30 centroid locations per second which means even if there the object crosses the line, it may never be 170!
To counter this, one method that can be used is defining a line margin. This means now you have a line margin x before actual line (y = 170) and x after the line margin.
So if your object falls anywhere in the margin, you can increment the counter. Now the next big part would be to make a tracking mechanism wherein you collect the list of point for each object and check if it fell in the margin.

Converting wind direction in angles to text words

I have wind direction data coming from a weather vane, and the data is represented in 0 to 359 degrees.
I want to convert this into text format (compass rose) with 16 different directions.
Basically I want to know if there is a fast slick way to scale the angle reading to a 16 string array to print out the correct wind direction without using a bunch of if statements and checking for ranges of angles
Wind direction can be found here.
thanks!
EDIT :
Since there is an angle change at every 22.5 degrees, the direction should swap hands after 11.25 degrees.
Therefore:
349-360//0-11 = N
12-33 = NNE
34-56 = NE
Using values from 327-348 (The entire NNW spectrum) failed to produce a result for eudoxos' answer.
After giving it some thought I could not find the flaw in his logic, so i rewrote my own..
def degToCompass(num):
val=int((num/22.5)+.5)
arr=["N","NNE","NE","ENE","E","ESE", "SE", "SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"]
print arr[(val % 16)]
>>> degToCompass(0)
N
>>> degToCompass(180)
S
>>> degToCompass(720)
N
>>> degToCompass(11)
N
>>> 12
12
>>> degToCompass(12)
NNE
>>> degToCompass(33)
NNE
>>> degToCompass(34)
NE
STEPS :
Divide the angle by 22.5 because 360deg/16 directions = 22.5deg/direction change.
Add .5 so that when you truncate the value you can break the 'tie' between the change threshold.
Truncate the value using integer division (so there is no rounding).
Directly index into the array and print the value (mod 16).
Here's a javascript implementation of steve-gregory's answer, which works for me.
function degToCompass(num) {
var val = Math.floor((num / 22.5) + 0.5);
var arr = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"];
return arr[(val % 16)];
}
See his answer for an explanation of the logic.
This JavaScript will work for anyone who only needs 8 cardinal directions and would like corresponding arrows.
function getCardinalDirection(angle) {
const directions = ['↑ N', '↗ NE', '→ E', '↘ SE', '↓ S', '↙ SW', '← W', '↖ NW'];
return directions[Math.round(angle / 45) % 8];
}
Watch out for rounding, angles between 349...11 should be "N", therefore add half sector first (+(360/16)/2), then handle overflow over 360 by %360, then divide by 360/16:
["N","NNW",...,"NNE"][((d+(360/16)/2)%360)/(360/16)]
I checked this and it works very good and seems accurate.
Source: http://www.themethodology.net/2013/12/how-to-convert-degrees-to-cardinal.html by Adrian Stevens
public static string DegreesToCardinal(double degrees)
{
string[] caridnals = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "N" };
return caridnals[(int)Math.Round(((double)degrees % 360) / 45)];
}
public static string DegreesToCardinalDetailed(double degrees)
{
degrees *= 10;
string[] caridnals = { "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N" };
return caridnals[(int)Math.Round(((double)degrees % 3600) / 225)];
}
I believe it is easier to:
Shift the direction by 11.25
Add an "N" at the end of the direction list to handle the 'over 360',
DirTable = ["N","NNE","NE","ENE","E","ESE", "SE","SSE","S","SSW","SW","WSW", "W","WNW","NW","NNW",**"N"**];
wind_direction= DirTable[Math.floor((d+11.25)/22.5)];
If you arrived here and are only interested in breaking your degrees into one of 8 directions.
function degToCompass(num){
const val = Math.floor((num / 45) + 0.5);
const arr = ["N","NE","E", "SE","S","SW","W","NW"];
return arr[(val % 8)]
Here's a one-line python function:
def deg_to_text(deg):
return ["N","NNE","NE","ENE","E","ESE", "SE", "SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"][round(deg/22.5)%16]
Obviously it can be split into multiple lines for readability/pep8
I would probably just do simple division of degrees to get a position in an array or an enum value or something that would give you the text you need. Just round down on all your division. 360/16 = 22.5, so you would want to divide by 22.5 to get the position.
String[] a = [N,NNW,NW,WNW,...,NNE]
this works fine
#!/usr/bin/env python
def wind_deg_to_str1(deg):
if deg >= 11.25 and deg < 33.75: return 'NNE'
elif deg >= 33.75 and deg < 56.25: return 'NE'
elif deg >= 56.25 and deg < 78.75: return 'ENE'
elif deg >= 78.75 and deg < 101.25: return 'E'
elif deg >= 101.25 and deg < 123.75: return 'ESE'
elif deg >= 123.75 and deg < 146.25: return 'SE'
elif deg >= 146.25 and deg < 168.75: return 'SSE'
elif deg >= 168.75 and deg < 191.25: return 'S'
elif deg >= 191.25 and deg < 213.75: return 'SSW'
elif deg >= 213.75 and deg < 236.25: return 'SW'
elif deg >= 236.25 and deg < 258.75: return 'WSW'
elif deg >= 258.75 and deg < 281.25: return 'W'
elif deg >= 281.25 and deg < 303.75: return 'WNW'
elif deg >= 303.75 and deg < 326.25: return 'NW'
elif deg >= 326.25 and deg < 348.75: return 'NNW'
else: return 'N'
def wind_deg_to_str2(deg):
arr = ['NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N']
return arr[int(abs((deg - 11.25) % 360)/ 22.5)]
i = 0
while i < 360:
s1 = wind_deg_to_str1(i)
s2 = wind_deg_to_str2(i)
print '%5.1f deg -> func1(%-3s), func2(%-3s), same:%s' % (i, s1, s2, ('ok' if s1 == s2 else 'different'))
i += 0.5
To do the reverse conversion (compass letter abbreviations to degrees):
function getDir($b)
{
$dirs = array('N'=>0, 'NNE'=>22.5,"NE"=>45,"ENE"=>67.5, 'E'=>90,'ESE'=>112.5, 'SE'=>135,'SSE'=>157.5, 'S'=>180,'SSW'=>202.5, 'SW'=>225,'WSW'=>247.5, 'W'=>270,'WNW'=>292.5,'NW'=>315,'NNW'=>337.5, 'N'=>0,'North'=>0,'East'=>90,'West'=>270,'South'=>180);
return $dirs[$b];
}
Javascript function 100% working
function degToCompass(num) {
while( num < 0 ) num += 360 ;
while( num >= 360 ) num -= 360 ;
val= Math.round( (num -11.25 ) / 22.5 ) ;
arr=["N","NNE","NE","ENE","E","ESE", "SE",
"SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"] ;
return arr[ Math.abs(val) ] ;
}
steps
Given a 360 degree angle
Since north is between -11.25 to 11.25 we subtract 11.25 for accuracy
Divide the angle by 22.5 because 360deg/16 directions = 22.5deg/direction change
Math.abs for as negative is still north
Select the segment from arr from answer
Hope it helps
Used this in Excel:
VLOOKUP(MROUND(N12,22.5),N14:O29,2,FALSE)
Cell N12 is direction toward in degrees for which an answer is needed.
The range N14:O29 is looking up the sector(A to R):
WIND SECTOR
0 A
22.5 B
45 C
67.5 D
90 E
112.5 F
135 G
157.5 H
180 J
202.5 K
225 L
247.5 M
270 N
292.5 P
315 Q
337.5 R
I use R heavily and needed a solution for this. This is what I came up with and works well for all possible combinations I have fed it:
degToCardinal <- function(degrees) {
val <- as.integer((degrees / 22.5) + 0.5)
arr <- c("N","NNE","NE","ENE","E","ESE", "SE", "SSE","S","SSW","SW","WSW","W","WNW","NW","NNW")
return(arr[((val+1) %% 16)])
}
Wanted to use #eudoxos but needed to pull all the parts together:
def deg_to_compass(d):
return ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"] [math.floor(((d+(360/16)/2)%360)/(360/16))]
Borrrowed #Hristo markow to check the results:
for i in range(0,360):
print (i,deg_to_compass(i) == wind_deg_to_str2(i))
compass_direction =["NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW","N"]
for i in range (0,365):
index = (int) ((((i / 11.25) - 1) /2) % 16)
print(f"Angle: {i:3}, Index: {index}, Compass: {compass_direction[index]}")