英文:
How can I plot street density over a polar histogram in QGIS or another GIS program?
问题
街道密度在极坐标直方图上
我想创建一个图表,定性地捕捉街道布局的某些特征(方向性、密度)。我已经在GIS中尝试了“极坐标直方图”,这是朝着正确方向迈出的一步,但它们具有明显的限制。因为只考虑线段的方向而不是位置,它们总是对称的,这不能很好地表示具有明确首选方向的街道布局(见图)。
使用工具“空间分析”(“线密度”),我已经确定了像素中线段的密度。有没有人知道如何将这个栅格文件转换成QGIS中的极坐标直方图,或者使用另一个程序?
这里有一种方式可以绘制街道位置的密度 - 与方向不同 - 作为原点(中心)角度的函数,覆盖在图中所示的同一个极坐标直方图上吗?
谢谢!
英文:
Street density over polar histogram
I want to create a graph that qualitatively captures some characteristics of a street layout (directionality, density). I have tried “polar histograms” in GIS, which are a step in the right direction, however come with some clear limitations. Because only orientation and not location of line segments is considered, they are always symmetrical, which doesn’t represent street layouts with clear preferred directions very well (see figure).
With the tool "spatial analyst" ("line density") I have already determined the density of the lines in a pixel. Does anyone know how to convert this raster file into a polar histogram in QGIS or with another programme?
[enter image description here](https://i.stack.imgur.com/2AEOS.png)
Is there any way to plot density of street location - as opposed to orientation - as function of angle from origin (center) over the same polar histogram seen in the figure?
thanks!
答案1
得分: 0
我发现你的问题非常有趣,我认为它适合作为一个不错的小项目。我手头上有一些代码,我为类似的问题制作过,我将其适应到了你的情况。代码中有注释,所以我认为它大部分都是自解释的。当以你的帖子中的png作为输入时,它会生成下面显示的输出:
import numpy as np
import matplotlib.ticker as plticker
from matplotlib import pyplot as plt
# 读取图像
im = plt.imread('2AEOS.png')
# 移除第四列
im = im[:,:,0:3]
# 对颜色通道取平均值
im = np.mean(im,axis=2)
# 识别图像的尺寸和中心点
dimensions = np.array(np.shape(im))
origin = np.round(np.flipud(dimensions)/2)
# 绘制输入图像和中心点
f = plt.figure(1)
plt.gray()
plt.imshow(im)
plt.plot(origin[0],origin[1],'rs')
plt.show()
# 计算每个像素向量从原点到x轴的角度
# np.arctan2函数按顺序接受坐标参数[y,x]
degrees = np.zeros(np.shape(im))
for y in range(dimensions[0]):
for x in range(dimensions[1]):
point = np.array([x,y])
v_origin_point = point-origin
degrees[y,x] = np.degrees(np.arctan2(v_origin_point[1],v_origin_point[0]))
# 选择分组数量
bin_number = 40
lspace = np.linspace(-180,180,bin_number+1)
vals = np.zeros(bin_number)
for i in range(len(lspace)-1):
# 识别分组的下限和上限
low = lspace[i]
high = lspace[i+1]
# 在“im”中汇总“degrees”在范围low-high内的像素强度
vals[i] = np.sum(im[(degrees>=low) & (degrees<high)])
# 计算圆形图的角度和半径
N = bin_number
theta = np.linspace(np.pi, -np.pi, N, endpoint=False)
# 分组的表面积与vals中的值成比例
radii = np.sqrt(vals)
width = 2*np.pi*np.ones(N)/N
# 在matplotlib中绘制图
g = plt.figure(2)
ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# 选择xticks的间距并删除yticks
loc = plticker.MultipleLocator(base=np.pi/4)
ax.xaxis.set_major_locator(loc)
ax.set_yticks([])
plt.show()
直方图是通过为每个像素分配其向中心点和x轴之间的角度(以度为单位)来获得的,如下所示
并汇总它在角度间隔内找到的像素强度(如果你愿意,可以将其视为图像的一个扇形片)。只需注意,像素强度的总和与直方图区域的表面积有关,而不是它们的半径。你可以通过将关系更改为radii = vals
来进行调整。
英文:
I found your question quite interesting and I think it makes for a nice pet project. I have some code at hand that I made for a similar problem that I adapted to your situation. Comments are provided in the code, so I think its mostly self-explanatory. When providing the png from your post as input, it generates the outputs shown below:
import numpy as np
import matplotlib.ticker as plticker
from matplotlib import pyplot as plt
# read image
im = plt.imread('2AEOS.png')
# remove 4th column
im = im[:,:,0:3]
# take average over color channels
im = np.mean(im,axis=2)
# identify dimensions of image and center point
dimensions = np.array(np.shape(im))
origin = np.round(np.flipud(dimensions)/2)
# draw input image and center point
f = plt.figure(1)
plt.gray()
plt.imshow(im)
plt.plot(origin[0],origin[1],'rs')
plt.show()
# calculate angle of every pixels vector from origin to x-axis
# np.arctan2 function takes coordinate args in order [y,x]
degrees = np.zeros(np.shape(im))
for y in range(dimensions[0]):
for x in range(dimensions[1]):
point = np.array([x,y])
v_origin_point = point-origin
degrees[y,x] = np.degrees(np.arctan2(v_origin_point[1],v_origin_point[0]))
# choose bin number
bin_number = 40
lspace = np.linspace(-180,180,bin_number+1)
vals = np.zeros(bin_number)
for i in range(len(lspace)-1):
# identify lower and upper bound of bin
low = lspace[i]
high = lspace[i+1]
# sum pixel intensities in "im" where "degrees" is within range low-high
vals[i] = np.sum(im[(degrees>=low) & (degrees<high)])
# Calculate thetas and ratii for circular plot
N = bin_number
theta = np.linspace(np.pi, -np.pi, N, endpoint=False)
# SURFACE AREA OF BIN SCALES WITH VALUE IN vals
radii = np.sqrt(vals)
width = 2*np.pi*np.ones(N)/N
# Plot in matplotlib
g = plt.figure(2)
ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Choose spacing of xticks and remove yticks
loc = plticker.MultipleLocator(base=np.pi/4)
ax.xaxis.set_major_locator(loc)
ax.set_yticks([])
plt.show()
Street layout with centre-point
The histogram is obtained by assigning each pixel the angle (in degrees) between its vector to the centre-point and x-axis like so
Degrees over domain as greyscale
and summing up the pixel intensities that it finds in an interval of angles (a pizza slice of the image if you will). Just note that the sum of pixel intensity relates to the surface area of the histogram bins, not their radius. You can adapt this by changing the relation to radii = vals
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论