Creating rectangle polygons within Multipolygons with shapely - shapely

I'm looking for wa way to create a set of poylgons (rechtangles) within multipolygons along a line and evely space them like in the drawing.
I tried to to generate points and use them as midpoints for the polygon but the problem is that by creating a evenly spaced point raster its not possible to rotate in any other orientation but 180 degrees.
example
given is a mulitopolygon shapely object and the polygon defined by widght and height as well as the vertical and horizontal spacing between each polygon.
The polygons should only be placed within the multipolygon and not intersect.
extra question: Maybe there is a way to puth them along a vector so its possible to rotate the lines.

If your spacing distances and rectangle dimensions are in meters. Try the following.
import folium
import geopy
import numpy as np
from shapely.geometry import Polygon, MultiPoint
def get_rectangle_points(coordinates, bearing, width, height):
start = geopy.Point(coordinates)
hypotenuse = np.hypot(width/1000, height/1000)
d = geopy.distance.distance(kilometers=hypotenuse/2)
opposite_angle = np.degrees(np.arctan(width/height))
northeast_angle = 0 - opposite_angle
southwest_angle = 180 - opposite_angle
northwest_angle = opposite_angle
southeast_angle = 180 + opposite_angle
points = []
for angle in [northeast_angle, northwest_angle, southwest_angle, southeast_angle]:
point = d.destination(point=start, bearing=angle+bearing)
coords = (point.latitude, point.longitude)
#coords = (point.longitude, point.latitude)
points.append(coords)
return points
lat_point_list = [50.854457, 52.518172, 50.072651, 48.853033, 50.854457]
lon_point_list = [4.377184, 13.407759, 14.435935, 2.349553, 4.377184]
polygon_geom = Polygon(zip(lon_point_list, lat_point_list))
latmin, lonmin, latmax, lonmax = polygon_geom.bounds
v = 100 #vertical spacing in meters
h = 500 #horizontal spacing in meters
height = 20000
width = 50000
vertical_spacing = geopy.units.degrees(arcminutes=geopy.units.km(meters=2*v+height)) #spacing in degrees
horizontal_spacing = geopy.units.degrees(arcminutes=geopy.units.km(meters=2*h+width)) #spacing in degrees
x, y = np.round(np.meshgrid(np.arange(latmin, latmax, horizontal_spacing), np.arange(lonmin, lonmax, vertical_spacing)),4)
points = MultiPoint(list(zip(x.flatten(),y.flatten())))
valid_points = []
valid_points.extend([i for i in points if polygon_geom.contains(i)])
map_coords = [polygon_geom.centroid.y, polygon_geom.centroid.x]
webmap = folium.Map(map_coords, zoom_start=6)
folium.GeoJson(polygon_geom).add_to(webmap)
for point in valid_points:
coords = (point.y, point.x)
rect_coords = get_rectangle_points(coords, 0, width, height) #increase bearing to rotate rectangle
folium.Polygon(rect_coords, color='red').add_to(webmap)
webmap.save('webmap.html')

Related

How to use GeoPandas or Fiona to find the inner part of the polygon through subtraction?

enter image description here
enter image description here
I am trying to find a way how we can get the upper part of the polygon (image 2) from the bigger polygon (image 1) using GeoPandas/Fiona functions. The other way round is quite easy by using the overlay set operation of "difference" but the way I want it, the functions/tools in GeoPandas does not work.
simply you need https://geopandas.org/en/stable/docs/user_guide/set_operations.html#
you have not provided any geometry. Have used Ukraine as larger geometry and a number of box geometries that overlap Ukraine
full example below
import shapely
import geopandas as gpd
import geopy.distance
import numpy as np
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
dims = (10, 10)
a, b, c, d = world.loc[world["name"].eq("Ukraine")].total_bounds
g = .1
# generate some polygons and a line that cuts through some of the polygons
gdf_grid = gpd.GeoDataFrame(
geometry=[
shapely.geometry.box(minx + g, miny + g, maxx - g, maxy - g)
for minx, maxx in zip(
np.linspace(a, c, dims[0]), np.linspace(a, c, dims[0])[1:]
)
for miny, maxy in zip(
np.linspace(b, d, dims[1]), np.linspace(b, d, dims[1])[1:]
)
],
crs="epsg:4326",
).sample(8, random_state=1)
big_without_small = world.loc[world["name"].eq("Ukraine")].overlay(gdf_grid, how="difference")
big_without_small.plot()

Build geometry on a rotated face

I've written a very basic script in Blender that builds a circular set of vertices then rotates and translates these to a rotated face in 3D space (see code below). This works well for any Z rotation however for X or Y rotations it's all over the place.
I know I'm missing a step somewhere but can't see it at the moment. So any help is much appreciated.
To run the code, there simply needs to be a cube in the scene.
The idea is that I build the circle of vertices at (0,0,0) then rotate these to the required face normal (I've chosen face 2 of the cube at random for now), then I translate the vertices to the face location. The main rotation function is at the bottom of the code (rotate_verts_towards_face(..))
NB. This is part of a larger project, so I've written this as a separate script as a demonstration only, so it's rough code.
(results of current code)
Face rotation at 0 degrees Z axis
Face rotation at 45 degrees Z axis
Face rotated at 45 degrees X axis
import bpy
import bmesh
import math
from mathutils import Vector
verts_norm = Vector((0, 0, 1))
verts_center = Vector((0, 0, 0))
# FACE BUILDING
def build_face_verts():
theta = 2 * math.pi / 12
dx = 0
dy = 0
dz = 0
verts = []
for i in range(12):
dx = 1 * math.sin(theta * i)
dy = 1 * math.cos(theta * i)
verts.append(Vector((dx, dy, dz)))
return verts
def update_bm_verts(bm):
bm.verts.index_update()
bm.verts.ensure_lookup_table()
def setup_new_obj(name, context):
obj_name = name
mesh = bpy.data.meshes.new("mesh")
obj = bpy.data.objects.new(obj_name, mesh)
context.scene.collection.objects.link(obj)
context.view_layer.objects.active = obj
obj.select_set(True)
mesh = context.object.data
bm = bmesh.new()
return obj, mesh, bm
def build_face(verts, context):
obj, mesh, bm = setup_new_obj("NEW_FACE", context)
for v in verts:
bm.verts.new(v)
update_bm_verts(bm)
bm.to_mesh(mesh)
bm.free()
# ROTATE AND TRANSLATE
def translate(pt, verts):
for v in verts:
v[0] += pt[0]
v[1] += pt[1]
v[2] += pt[2]
def rotate_verts_towards_face(verts, target, target_norm, target_loc):
mat_world = target.matrix_world
trans_verts = []
# transform face verts to target object space
trans_verts = [mat_world # v for v in verts]
# build rotation matrix
mat = (
verts_norm.rotation_difference(target_norm).to_matrix().to_4x4()
)
return [mat # v for v in trans_verts]
target_obj = bpy.data.objects["Cube"]
target_face_norm = target_obj.data.polygons[2].normal
mat_world = target_obj.matrix_world
target_face_norm = mat_world # target_face_norm
target_location = target_obj.data.polygons[2].center
# rotate verts
r_verts = rotate_verts_towards_face(build_face_verts(), target_obj, target_face_norm, target_location)
# translate verts to face position
translate(target_face_norm, r_verts)
build_face(r_verts, bpy.context)
Sorry to answer my own question but I figured this out. It's a simple fix that just involves translating the verts_normal by the target object's matrix world. The rotation code becomes:
norm = mat_world # verts_norm
# build rotation matrix
mat = (
norm.rotation_difference(target_norm).to_matrix().to_4x4()
)

Finding if a circle is fully contained within multiple triangles?

In a game, an area is defined by triangles that never overlap, and characters are defined by circles.
How can I know whether the full character's collision circle is contained within these triangles?
Example image:
Here, the red parts are outside triangles, so the circle isn't contained within them. Is there an algorithm that can detect this?
I've only came up with "non-perfect" solutions, like sampling points at the border of the circle, then testing if each is inside a triangle.
So basically, the triangles form a domain with polygonal boundary and you want to check if a disk, defined by a center point and a radius is contained inside the domain. So if you start with the triangles, you have to find a way to extract the polygonal boundary of your domain and represent it as a 2D array (matrix) of shape n rows and two columns so that every row is the two coordinates of a vertex point of the polygonal boundary line and the points are ordered so that they are consecutive order along the boundary in a counterclockwise position, i.e. when you walk in a direction from point of index i to the next point i+1 the domain stays on your left. For example, here is the representation of a polygonal boundary of a domain like yours:
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
Observe that the first and the last points are the same.
In such a scenario, maybe you can try the following algorithm:
import numpy as np
import math
def angle_and_dist(p1, p2, o):
p12 = p2 - p1
op1 = p1 - o
op2 = p2 - o
norm_p12 = math.sqrt(p12[0]**2 + p12[1]**2)
norm_op1 = math.sqrt(op1[0]**2 + op1[1]**2)
norm_op2 = math.sqrt(op2[0]**2 + op2[1]**2)
p12_perp = np.array([ - p12[1], p12[0] ])
h = - op1.dot(p12_perp)
theta12 = op1.dot(op2) / (norm_op1*norm_op2)
theta12 = math.acos( theta12 )
if h < 0:
theta12 = - theta12
if op1.dot(p12) > 0:
return theta12, norm_op1
elif op2.dot(p12) < 0:
return theta12, norm_op2
else:
return theta12, h/norm_p12
def is_in_polygon(p, disk):
o, r = disk
n_p = len(p)-1
index_o = 0
h_min = 400
for i in range(n_p):
theta, h = angle_and_dist(p[i,:], p[i+1,:], o)
index_o = index_o + theta
if 0 <= h and h < h_min:
h_min = h
if theta <= math.pi/100:
return 'center of disc is not inside polygon'
elif theta > math.pi/100:
if h_min > r:
return 'disc is inside polygon'
else:
return 'center of disc is inside polygon but disc is not'
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
# A test example:
#disc = (np.array([3*a/4, 2]), a/4-0.001)
disc = (np.array([3*a/4, 2]), math.sqrt(3)*a/8 - 0.0001)
print(is_in_polygon(Pgon, disc))

Applying a rotation matrix on a point with an origin fails

From this answer; https://stackoverflow.com/a/34060479/1493455 I have made some code that should rotate a point around an origin point. The origin is at 0,0,0.
Instead of my rotations being yaw pitch roll, I have xrot yrot and zrot which are degrees (0-360). A rotation around the X axis results in the object pitching back and forth, a rotation around the Z axis results in it rotating as if it was the yaw of the object.
I think I messed up somewhere in translating the values, but I can't seem to figure out why.
When rotating the points around each seperate angle (keeping the other 2 on 0 degrees) every rotation gives the right solution.
Combined, the results are good when rotating over X and Z or Y and Z. The results are not correct when rotating around X and Y.
var cosa = cos(-degtorad(zrot));
var sina = sin(-degtorad(zrot));
var cosb = cos(-degtorad(yrot));
var sinb = sin(-degtorad(yrot));
var cosc = cos(-degtorad(xrot));
var sinc = sin(-degtorad(xrot));
var Axx = cosa*cosb;
var Axy = cosa*sinb*sinc - sina*cosc;
var Axz = cosa*sinb*cosc + sina*sinc;
var Ayx = sina*cosb;
var Ayy = sina*sinb*sinc + cosa*cosc;
var Ayz = sina*sinb*cosc - cosa*sinc;
var Azx = -sinb;
var Azy = cosb*sinc;
var Azz = cosb*cosc;
var px = Axx*x + Axy*y + Axz*z;
var py = Ayx*x + Ayy*y + Ayz*z;
var pz = Azx*x + Azy*y + Azz*z;
degtorad is basically return input * pi / 180
I feel like there is an issue with the order of the calculation - since individual rotations give the proper outcome, just the combination of these 3 don't.
I know this because I'm rotating a 3D model using the zrot, yrot and xrot values, and drawing the point in the same 3D space - the points line up with the model in most cases, but not rotations on X and Y, or X, Y and Z.
VIDEO OF RESULT: https://streamable.com/481ly (there are 2 points on the corners of that cupboard which are calculated through this code)

three.js rotate object by an axe direction

I want to rotate a cylinder in a certain axe made by two points p1 and p2.
I create the cylinder with the height l equal to the distance between the two points, I place it in the middle of that axe.
var xd = p2.x - p1.x,
yd = p2.y - p1.y,
zd = p2.z - p1.z,
l = Math.sqrt(xd*xd + yd*yd + zd*zd);
var cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 5, 5, l, 32 ), new THREE.MeshBasicMaterial( {color: "#ffffff"} ) );
cylinder.position.set(p1.x+xd/2, p1.y+yd/2, p1.z+zd/2);
I use setFromUnitVectors to get the rotaion matrix needed between the two points and apply it to the rotation matrix of the cylinder
var quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(p1.x,p1.y,p1.z).normalize(),new THREE.Vector3(p2.x,p2.y,p2.z).normalize());
cylinder.rotation.setFromQuaternion(quaternion);
I dont see what is wrong or maybe there is another way to do it?
another way to do this is to just make the cylinder LookAt(p2) if p2 is a vector.

Resources