Doğu-batı yönünde bir gölün maksimum genişliğiyle ilgileniyorum. Sınırlayıcı kutular sadece basit çokgenlerde yardımcı olur, ancak karmaşık içbükey çokgenlerde yardımcı olmaz.
Doğu-batı yönünde bir gölün maksimum genişliğiyle ilgileniyorum. Sınırlayıcı kutular sadece basit çokgenlerde yardımcı olur, ancak karmaşık içbükey çokgenlerde yardımcı olmaz.
Yanıtlar:
Bu muhtemelen herhangi bir CBS platformunda bazı komut dosyaları gerektirir.
En verimli yöntem (asimptotik olarak) dikey bir çizgi taramasıdır: kenarları minimum y koordinatlarına göre sıralamayı ve ardından kenarları alttan (minimum y) yukarı (maksimum y), bir O (e * günlüğü ( e)) e kenarlar söz konusu olduğunda algoritma .
Prosedür, her ne kadar basit olsa da, her durumda doğru bir şekilde elde etmek şaşırtıcı derecede zordur. Çokgenler kötü olabilir: sarkmalara, şeritlere, deliklere sahip olabilirler, bağlantı kesilebilirler, çoğaltılmış köşeleri, düz çizgiler boyunca köşeler ve iki bitişik bileşen arasında çözülmemiş sınırları olabilir. İşte bu özelliklerin çoğunu (ve daha fazlasını) gösteren bir örnek:
Özellikle tamamen çokgenin kapatılması içinde bulunan maksimum uzunluktaki yatay segment (ler) i arayacağız . Örneğin bu, x = 10 ve x = 25 arasındaki delikten çıkan x = 20 ve x = 40 arasındaki salgıyı ortadan kaldırır. Daha sonra, maksimum uzunluktaki yatay segmentlerden en az birinin en az bir tepe ile kesiştiğini göstermek kolaydır. (Hayır köşe kesişen çözümler varsa, bunlar çözümlerle altta ve üstte sınırlanmış bazı paralelkenar iç yatacak yapmak en az bir tepeye kesiştiği. Bu bize bulmak için bir araç verir bütün çözümleri.)
Buna göre, çizgi taraması en düşük köşelerle başlamalı ve sonra her bir tepe noktasında durmak için yukarı doğru (yani daha yüksek y değerlerine doğru) hareket etmelidir. Her durakta, o yükseklikten yukarı doğru çıkan yeni kenarlar buluyoruz; bu yükseklikte aşağıdan sonlanan kenarları ortadan kaldırın (bu, anahtar fikirlerden biridir: algoritmayı basitleştirir ve potansiyel işlemenin yarısını ortadan kaldırır); ve tamamen sabit bir yükseklikte (yatay kenarlar) yatan kenarları dikkatlice işleyin.
Örneğin, y = 10 seviyesine ulaşıldığında durumu düşünün. Soldan sağa aşağıdaki kenarları buluruz:
x.min x.max y.min y.max
[1,] 10 0 0 30
[2,] 10 24 10 20
[3,] 20 24 10 20
[4,] 20 40 10 10
[5,] 40 20 10 10
[6,] 60 0 5 30
[7,] 60 60 5 30
[8,] 60 70 5 20
[9,] 60 70 5 15
[10,] 90 100 10 40
Bu tabloda, (x.min, y.min) kenarın alt uç noktasının koordinatlarıdır ve (x.max, y.max) üst uç noktasının koordinatlarıdır. Bu seviyede (y = 10), ilk kenar iç kısmında durdurulur, ikincisi alt kısmında durdurulur, vb. İçin, örneğin (10,0) itibaren bu düzeyde son bulan bazı kenarları (10,10), olan olmayan listeye dahil.
İç noktaların ve dış noktaların nerede olduğunu belirlemek için, elbette çokgenin dışında olan aşırı soldan başlayıp yatay olarak sağa doğru hareket ettiğinizi hayal edin. Yatay olmayan bir kenarı her geçtiğimizde , sırayla dıştan iç mekana ve arkaya geçeriz. (Bu başka bir önemli fikirdir.) Bununla birlikte, herhangi bir yatay kenardaki tüm noktaların, ne olursa olsun, çokgenin içinde olduğu belirlenir. (Çokgenin kapatılması her zaman kenarlarını içerir.)
Örneğe devam edersek, yatay olmayan kenarların y = 10 satırında başladığı veya geçtiği x koordinatlarının sıralı listesi aşağıdadır :
x.array 6.7 10 20 48 60 63.3 65 90
interior 1 0 1 0 1 0 1 0
(X = 40'ın bu listede olmadığına dikkat edin.) Dizinin değerleri interior
, iç segmentlerin sol uç noktalarını işaretler: 1, bir iç aralık, 0 bir dış aralık belirtir. Böylece, ilk 1 x = 6.7 ila x = 10 arasındaki aralığın çokgenin içinde olduğunu gösterir. Sonraki 0, x = 10 ile x = 20 arasındaki aralığın çokgenin dışında olduğunu gösterir . Ve böylece devam eder: dizi, çokgenin içindeki gibi dört ayrı aralık tanımlar.
Bu aralıkların bazıları, örneğin x = 60 ila x = 63.3 gibi, herhangi bir köşeyi kesmez: y = 10 ile tüm köşelerin x koordinatlarına karşı hızlı bir kontrol, bu aralıkları ortadan kaldırır.
Tarama sırasında, şimdiye kadar bulunan maksimum uzunluk aralıkları ile ilgili verileri koruyarak bu aralıkların uzunluklarını izleyebiliriz.
Bu yaklaşımın bazı etkilerine dikkat edin. Bir "v" şekilli tepe noktası, karşılaşıldığında, iki kenarın kökenidir. Bu nedenle , geçerken iki anahtar oluşur. Bu anahtarlar iptal edilir. Herhangi bir baş aşağı "v" bile işlenmez, çünkü soldan sağa taramaya başlamadan önce her iki kenarı da ortadan kaldırılır. Her iki durumda da, böyle bir tepe yatay bir parçayı engellemez.
İkiden fazla kenar bir tepe noktasını paylaşabilir: bu (10,0), (60,5), (25, 20) 'de gösterilmiştir ve - söylemek zor olsa da (20,10) ve (40) , 10). (Çünkü sarkan gider (20,10) -> (40,10) -> (40,0) -> (40, -50) -> (40, 10) -> (20, (40,0) 'daki tepe noktasının da başka bir kenarın iç kısmında nasıl olduğuna dikkat edin ... bu çok kötü.) Bu algoritma bu durumları gayet iyi idare ediyor.
Zor bir durum en altta gösterilmiştir: yatay olmayan segmentlerin x koordinatları vardır
30, 50
Bu, x = 30'un solundaki her şeyin dış olarak kabul edilmesine, 30 ile 50 arasındaki her şeyin iç olmasına ve 50'den sonraki her şeyin tekrar dış olmasına neden olur. X = 40'taki tepe noktası bu algoritmada asla dikkate alınmaz.
Taramanın sonunda çokgenin görüntüsü. Köşe içeren tüm iç aralıkları koyu gri, maksimum uzunluk aralığını kırmızı olarak gösterir ve köşeleri y koordinatlarına göre renklendiririm. Maksimum aralık 64 birim uzunluğundadır.
İlgili tek geometrik hesaplamalar kenarların yatay çizgilerle kesiştiği yeri hesaplamaktır: bu basit bir doğrusal enterpolasyondur. Hesaplamalar ayrıca iç kesimleri köşe içeren belirlemek için gereklidir: bunlar betweenness kolayca eşitsizliklerin bir çift hesaplanan tespitler. Bu basitlik algoritmayı hem tamsayı hem de kayan nokta koordinat gösterimleri için sağlam ve uygun hale getirir.
Koordinatlar coğrafi ise , yatay çizgiler gerçekten enlem dairelerinde bulunur. Uzunluklarını hesaplamak zor değildir: Öklid uzunluklarını enlemlerinin kosinüsü ile çarpın (küresel bir modelde). Bu nedenle bu algoritma coğrafi koordinatlara iyi uyum sağlar. (+ -180 meridyen kuyusunun etrafına sarmak için, önce güney kutbundan kuzey kutbuna çokgenden geçmeyen bir eğri bulunması gerekebilir. Tüm x-koordinatlarını buna göre yatay yer değiştirmeler olarak tekrar ifade ettikten sonra eğri, bu algoritma maksimum yatay segmenti doğru bir şekilde bulur.)
R
Hesaplamaları yapmak ve resimleri oluşturmak için uygulanan kod aşağıdadır .
#
# Plotting functions.
#
points.polygon <- function(p, ...) {
points(p$v, ...)
}
plot.polygon <- function(p, ...) {
apply(p$e, 1, function(e) lines(matrix(e[c("x.min", "x.max", "y.min", "y.max")], ncol=2), ...))
}
expand <- function(bb, e=1) {
a <- matrix(c(e, 0, 0, e), ncol=2)
origin <- apply(bb, 2, mean)
delta <- origin %*% a - origin
t(apply(bb %*% a, 1, function(x) x - delta))
}
#
# Convert polygon to a better data structure.
#
# A polygon class has three attributes:
# v is an array of vertex coordinates "x" and "y" sorted by increasing y;
# e is an array of edges from (x.min, y.min) to (x.max, y.max) with y.max >= y.min, sorted by y.min;
# bb is its rectangular extent (x0,y0), (x1,y1).
#
as.polygon <- function(p) {
#
# p is a list of linestrings, each represented as a sequence of 2-vectors
# with coordinates in columns "x" and "y".
#
f <- function(p) {
g <- function(i) {
v <- p[(i-1):i, ]
v[order(v[, "y"]), ]
}
sapply(2:nrow(p), g)
}
vertices <- do.call(rbind, p)
edges <- t(do.call(cbind, lapply(p, f)))
colnames(edges) <- c("x.min", "x.max", "y.min", "y.max")
#
# Sort by y.min.
#
vertices <- vertices[order(vertices[, "y"]), ]
vertices <- vertices[!duplicated(vertices), ]
edges <- edges[order(edges[, "y.min"]), ]
# Maintaining an extent is useful.
bb <- apply(vertices <- vertices[, c("x","y")], 2, function(z) c(min(z), max(z)))
# Package the output.
l <- list(v=vertices, e=edges, bb=bb); class(l) <- "polygon"
l
}
#
# Compute the maximal horizontal interior segments of a polygon.
#
fetch.x <- function(p) {
#
# Update moves the line from the previous level to a new, higher level, changing the
# state to represent all edges originating or strictly passing through level `y`.
#
update <- function(y) {
if (y > state$level) {
state$level <<- y
#
# Remove edges below the new level from state$current.
#
current <- state$current
current <- current[current[, "y.max"] > y, ]
#
# Adjoin edges at this level.
#
i <- state$i
while (i <= nrow(p$e) && p$e[i, "y.min"] <= y) {
current <- rbind(current, p$e[i, ])
i <- i+1
}
state$i <<- i
#
# Sort the current edges by x-coordinate.
#
x.coord <- function(e, y) {
if (e["y.max"] > e["y.min"]) {
((y - e["y.min"]) * e["x.max"] + (e["y.max"] - y) * e["x.min"]) / (e["y.max"] - e["y.min"])
} else {
min(e["x.min"], e["x.max"])
}
}
if (length(current) > 0) {
x.array <- apply(current, 1, function(e) x.coord(e, y))
i.x <- order(x.array)
current <- current[i.x, ]
x.array <- x.array[i.x]
#
# Scan and mark each interval as interior or exterior.
#
status <- FALSE
interior <- numeric(length(x.array))
for (i in 1:length(x.array)) {
if (current[i, "y.max"] == y) {
interior[i] <- TRUE
} else {
status <- !status
interior[i] <- status
}
}
#
# Simplify the data structure by retaining the last value of `interior`
# within each group of common values of `x.array`.
#
interior <- sapply(split(interior, x.array), function(i) rev(i)[1])
x.array <- sapply(split(x.array, x.array), function(i) i[1])
print(y)
print(current)
print(rbind(x.array, interior))
markers <- c(1, diff(interior))
intervals <- x.array[markers != 0]
#
# Break into a list structure.
#
if (length(intervals) > 1) {
if (length(intervals) %% 2 == 1)
intervals <- intervals[-length(intervals)]
blocks <- 1:length(intervals) - 1
blocks <- blocks - (blocks %% 2)
intervals <- split(intervals, blocks)
} else {
intervals <- list()
}
} else {
intervals <- list()
}
#
# Update the state.
#
state$current <<- current
}
list(y=y, x=intervals)
} # Update()
process <- function(intervals, x, y) {
# intervals is a list of 2-vectors. Each represents the endpoints of
# an interior interval of a polygon.
# x is an array of x-coordinates of vertices.
#
# Retains only the intervals containing at least one vertex.
between <- function(i) {
1 == max(mapply(function(a,b) a && b, i[1] <= x, x <= i[2]))
}
is.good <- lapply(intervals$x, between)
list(y=y, x=intervals$x[unlist(is.good)])
#intervals
}
#
# Group the vertices by common y-coordinate.
#
vertices.x <- split(p$v[, "x"], p$v[, "y"])
vertices.y <- lapply(split(p$v[, "y"], p$v[, "y"]), max)
#
# The "state" is a collection of segments and an index into edges.
# It will updated during the vertical line sweep.
#
state <- list(level=-Inf, current=c(), i=1, x=c(), interior=c())
#
# Sweep vertically from bottom to top, processing the intersection
# as we go.
#
mapply(function(x,y) process(update(y), x, y), vertices.x, vertices.y)
}
scale <- 10
p.raw = list(scale * cbind(x=c(0:10,7,6,0), y=c(3,0,0,-1,-1,-1,0,-0.5,0.75,1,4,1.5,0.5,3)),
scale *cbind(x=c(1,1,2.4,2,4,4,4,4,2,1), y=c(0,1,2,1,1,0,-0.5,1,1,0)),
scale *cbind(x=c(6,7,6,6), y=c(.5,2,3,.5)))
#p.raw = list(cbind(x=c(0,2,1,1/2,0), y=c(0,0,2,1,0)))
#p.raw = list(cbind(x=c(0, 35, 100, 65, 0), y=c(0, 50, 100, 50, 0)))
p <- as.polygon(p.raw)
results <- fetch.x(p)
#
# Find the longest.
#
dx <- matrix(unlist(results["x", ]), nrow=2)
length.max <- max(dx[2,] - dx[1,])
#
# Draw pictures.
#
segment.plot <- function(s, length.max, colors, ...) {
lapply(s$x, function(x) {
col <- ifelse (diff(x) >= length.max, colors[1], colors[2])
lines(x, rep(s$y,2), col=col, ...)
})
}
gray <- "#f0f0f0"
grayer <- "#d0d0d0"
plot(expand(p$bb, 1.1), type="n", xlab="x", ylab="y", main="After the Scan")
sapply(1:length(p.raw), function(i) polygon(p.raw[[i]], col=c(gray, "White", grayer)[i]))
apply(results, 2, function(s) segment.plot(s, length.max, colors=c("Red", "#b8b8a8"), lwd=4))
plot(p, col="Black", lty=3)
points(p, pch=19, col=round(2 + 2*p$v[, "y"]/scale, 0))
points(p, cex=1.25)
İşte raster tabanlı bir çözüm. Hızlıdır (14 dakika içinde baştan sona tüm işleri yaptım), komut dosyası gerektirmez, sadece birkaç işlem yapar ve makul doğrudur.
Çokgenin raster gösterimi ile başlayın. Bu, 550 satır ve 1200 sütunluk bir ızgara kullanır:
Bu gösterimde, gri (iç) hücreler 1 değerine sahiptir ve diğer tüm hücreler NoData'dır.
Ağırlık ızgarası için birim hücre değerlerini ("yağış" miktarı) kullanarak batı-doğu yönünde akış birikimini hesaplayın :
Düşük birikim karanlıktır ve parlak sarıdaki en yüksek birikimlere yükselir.
Bir maksimum bölge (ızgara için çokgen ve değerler için akış birikimi kullanılarak), akışın en büyük olduğu hücreleri belirler. Bunları göstermek için sağ alta doğru yakınlaştırmak zorunda kaldım:
Kırmızı hücreler, en yüksek akış birikimlerinin uçlarını işaretler: bunlar, çokgenin maksimum uzunluktaki iç segmentlerinin en sağ uç noktalarıdır.
Bu segmentleri bulmak için, tüm ağırlıkları kırmızı hücrelere yerleştirin ve akışı geriye doğru çalıştırın!
Alt taraftaki kırmızı şerit iki sıra hücre işaretler: bunların içinde maksimum uzunluktaki yatay segment bulunur. Bu temsili daha fazla analiz için olduğu gibi kullanın veya çokgen (veya çokgen) şekle dönüştürün.
Raster temsili ile yapılan bazı takdir yetkisi hatası var. Hesaplama süresinde bir miktar maliyetle çözünürlüğü artırarak azaltılabilir.
Bu yaklaşımın gerçekten güzel bir yönü, tipik olarak, bazı hedeflere ulaşılması gereken daha büyük bir iş akışının parçası olarak şeylerin aşırı değerlerini bulmamızdır: bir boru hattı veya futbol sahası oturmak, ekolojik tamponlar oluşturmak vb. Süreç ödünleşmeleri içerir. Bu nedenle, en uzun yatay çizgi optimal bir çözümün parçası olmayabilir. Bunun yerine neredeyse en uzun çizgilerin nerede olacağını bilmek isteriz. Bu basit: bölgesel maksimum akışı seçmek yerine, bölgesel maksimuma yakın tüm hücreleri seçin. Bu örnekte, bölgesel maks 744'e (en uzun iç segment tarafından yayılan sütun sayısı) eşittir. Bunun yerine, bu maksimum değerin% 5'indeki tüm hücreleri seçelim:
Doğudan batıya doğru akışı yürütmek, bu yatay segment koleksiyonunu üretir:
Bu, çokgen içinde herhangi bir yerde kesintisiz doğu-batı boyutunun maksimum doğu-batı boyutundan% 95 veya daha fazla olduğu konumların haritasıdır.
Tamam. Başka (daha iyi) bir fikrim var ( fikir-№2 ). Ama bir SQL-querry olarak değil, bir python betiği olarak gerçekleştirilmesinin daha iyi olduğunu düşünüyorum. Yine burada ortak durum, sadece EW değil.
Çokgen için sınırlayıcı bir kutu ve ölçüm yönünüz olarak bir azimut (A) gerekir. BBox kenarlarının uzunluğunun LA ve LB olduğunu varsayın. Poligon içinde maksimum olası mesafe (MD) 'dir: MB = (LA^2 * LB^2)^(1/2)
, değer (V) arayan MB'den değil büyüktür böylece: V <= MB
.
Fetzer'in cevabının yapmak istediğiniz şey olduğundan emin değilim, ancak st_box2d işi yapabilir.
SS_Rebelious'un N ° 1 fikri birçok durumda işe yarar ancak bazı içbükey çokgenler için işe yaramaz.
Doğu-batı çizgi olasılığı varsa, köşe yapımı çizgiler çokgenin sınırlarını aştığında noktaları takip eden yapay lw-çizgileri oluşturmanız gerektiğini düşünüyorum.
Bunun için, satır uzunluğunun yüksek olduğu bir 4 düğümlü çokgen yapmaya çalışabilir, önceki poligonla üst üste binen poligon P * 'yı yapabilir ve min (y1) ve max (y2)' nin biraz x-çizgisi bırakıp bırakmadığını görebilirsiniz. olasılık. (burada y1, sol üst kornet ile sağ üst köşe arasındaki nokta kümesidir ve y2, 4 düğümlü çokgeninizin sol alt ve sağ alt köşeleri arasındaki y kümesidir). Bu o kadar kolay değil umarım size yardımcı olacak psql araçları bulacaksınız!
Bir fikrim №1 ( Düzenleme: ortak durum için, sadece EW yönü ve yorumlarda açıklanan bazı sınırlamalarla) var. Kodu vermeyeceğim, sadece bir kavram. "X-yönü" aslında ST_Azimuth tarafından hesaplanan bir azimuttur. Önerilen adımlar:
Benim göz at sorusuna ve cevap Evil Genius dan.
Umarım göl poligonunuzun birkaç noktası vardır, bu noktalarda azimut (en boy, coğrafi yön) ile çizgiler oluşturabilirsiniz. En uzak iki çizgi arasındaki en kısa çizgiyi hesaplayabilmeniz için çizgilerin uzunluğunu (ST_MakePoint bölümü) seçin.
İşte bir örnek:
Örnek, çokgenin maksimum genişliğini göstermektedir. Bu yaklaşım için ST_ShortestLine (kırmızı çizgi) seçiyorum. ST_MakeLine değeri (mavi çizgi) artıracak ve çizginin bitiş noktası (sol alt) çokgenin mavi çizgisine çarpacaktır. Mesafeyi, oluşturulan (yardım) çizgilerin centroidleri ile hesaplamanız gerekir.
Bu yaklaşım için düzensiz veya içbükey çokgenler için bir fikir. Belki poligonu bir rasterle kesmeniz gerekebilir.