Split Up Big Polygon Into Smaller Ones By Condition - algorithm

I have some big Polygon. Within it collection of leaflet layers (points). Every point have some numeric property. What I want is split up big Polygon into smaller ones.
Every smaller polygon should contain points with ~equal (+-200 ok) sum of points properties. On the left side of my example page I added image of desirable result.
Here is my simplified example with sufficient code and comments.
So my first step is find some start point within big Polygon. It's should be point near Polygon edge - the northernmost point for example.
var nothernmostPoint= 0;
var nothernmostLayer= 0;
L.geoJSON(features, {
pointToLayer: function (feature) {
return L.circleMarker(feature.geometry.coordinates.reverse(), defaultPointStyle);
},
onEachFeature: function (feature, layer) {
if (feature.geometry.coordinates[0] > nothernmostPoint) {
nothernmostPoint = feature.geometry.coordinates[0];
nothernmostLayer = feature;
}
}
}).addTo(map);
Second step is to find next nearest point(s) to my start point.
var geoJ = L.GeometryUtil.nClosestLayers(map, features, nothernmostLayer.geometry.coordinates, 5);
Then sum up their properties. If sum is smaller than needed I go to step 2 and repeat, if sum is satisfy condition then I draw polygon with chosen points inside it and find nearest point to my last point and repeat search for points for the next smaller polygon.
My current difficulty is to find nearest point(s) to my start point. For that purpose I use GeometryUtil Leaflet plugin. Red dots are points that GeometryUtil found as nearest to my start point (the green one). It's certainly not what I was expected. What am I doing wrong? Maybe I should use different algorithm and/or tool for that task? Any useful suggestions greatly appreciated.
If it may help - all that data I store in PostgreSQL with PostGIS extension. Maybe this can be done on database level.

If your dataset is rather small, you can go brute force way in PostGIS:
for each point, generate N points that represent the 'weight', using select geom, generate_series(0, weight);
decide on number of clusters you want to get, approximately sum(weight)/desired_sum;
run K-Means clustering on dataset, https://postgis.net/docs/manual-2.3/ST_ClusterKMeans.html
draw a polygon around each cluster using ST_ConvexHull(ST_Collect(geom)).

Related

Better "centerpoint" than centroid

I'm using the centroid of polygons to attach a marker in a map application. This works definitely fine for convex polygons and quite good for many concave polygons.
However, some polygons (banana, donut) obviously don't produce the desired result: The centroid is in these cases outside the polygons area.
Does anybody know a better approach to find a suitable point within any polygons area (which may contain holes!) to attach a marker?
One approach would be to generate and refine a skeleton of the polygon, then use the midpoint of the skeleton to place your marker (and if it's text, to orient the text correctly). This works well for most shapes, including ones with holes, and banana-shaped or tadpole-shaped crescents.
The CGAL library has a 2D Straight Skeleton and Polygon Offsetting module, or you could use PostGIS, for example.
To rephrase comment of ChristopheRoussy we may look for the largest circle inside of the polygon.
The largest circle is the one which cannot grow anymore because it touches three vertices or edges (if it touches only two, it can become bigger or just moved until it touches third).
So if you have few vertices, you can just enumerate all possible triples of vertices/edges, find for each one a circle and then select the largest one.
But it will require creating four functions:
Circle(vertex,vertex,vertex)
Circle(vertex,vertex,edge)
Circle(vertex,edge,edge)
Circle(edge,edge,edge)
All of them are possible, but may require some effort.
Find the extreme ordinates and draw an horizontal line in the middle. It is guaranteed to cross the polygon.
Find the intersection with the sides and sort them by increasing abscissa. Pick a point in the middle of two intersections.
This is an O(N + K Log K) process where K is the number of intersections (usually a very small even number). Pretty straightforward to write.
To increase the chances of a nice placement, you can try three horizontals instead of one an pick the longest intersection segment.
I have no idea how to solve this for any possible shape (and not doing heavy computation), but maybe for simpler shapes like the ones you have shown:
https://en.wikipedia.org/wiki/Force-directed_graph_drawing
Heuristic: This could converge to a reasonable approximation after a while
transform shape border into many points (more = more precise)
start out with many random points inside the polygon
push them until they are furthest away from border points, or just compute distance ... (can be done in parallel)
take best point
Another way could be to use multiple algorithms depending on the nature of the shape (like another one for donuts ...). Also perhaps relying on measuring 'fattest' sections first ?
IMHO would ask this on a math forum.
Similar: Calculate Centroid WITHIN / INSIDE a SpatialPolygon
Similar: How to find two most distant points?
To get a point for a marker I would use Yves Daoust's method.
To get a point that is reliably "within any polygon with holes" I would split polygon into triangles with a reliable library (e.g. OpenGL's GLUtessellator), and then get centroid of triangle with largest area.
If I had time for developing and testing, and I wanted good performance, then I would use a hybrid method: First use Yves Daoust's method to get some candidate points and then test candidates to see if they are within polygon. If all candidates fail, then fall back to slower reliable method (e.g. GLUtesselator).
for (int i = 0; i < n; /*++i*/) {
p = RandomPointInsideConvexHull();
if (IsInsidePolygon(p)) {
++i;
d = DistanceToClosestEdge(p);
if (d > bestD) {
bestP = p;
}
}
}
After running this loop you will approximate solution by bestP. n is parameter to choose. If you want more accurate result you can restart search, but now instead of picking a point inside polygon's convex hull you can pick one in the neighborhood of bestP, say not farther than bestD / 5 (this time you don't need to check if random point is inside polygon).

Matlab: how to generate non-uniform and non-overlap circle/rectangle in a circle with specific radius

I want to generate a series of points as building in simulation.
Points density is 1/1000m^3
The points have its shape just like the real buildings(circle or rectangle or something else)
In order to reach the reality, these shapes should not be overlapped.
The question is how to generate the center point of these 'buildings'?
I tried this
clusterNumber = round((pi*areaRadius^2)/1000);
radius = unifrnd (0,areaRadius,clusterNumber,1);
angle = unifrnd (-pi,pi,clusterNumber,1);
for i=1:clusterNumber
Coordinate(i,1) = cos(angle(i))*radius(i); % x
Coordinate(i,2) = sin(angle(i))*radius(i); % y
and the result showed as what I expected... it did'nt work
When I used scatter it showed
So, my question is how to generate non-uniform and non-overlap circles or rectangles in a specific circle.
If you want your buildings not to intersect, you must check for intersections with already created buildings before creating one at your random position.
Of course, if you create many buildings, collision detection will be costly. You can speed it up with an efficient nearest neighbour search, for example with kd-trees or by creating a fine grid in the building space so that you have only a few neighbour cells to check.
Imposing the condition that buildings must not intersect will also alter your distribution. You will no longer have the marked clustering in the circle's centre. You still generate morre random positionsthere, but as your area gets more populated, most of them will be rejected.
Here's an example distribution:
Enforcing the criterion may also affect your algorithm: It might be a good idea to limit the number of randomly gerenated positions, so that you don't run into an infinite loop when no more buildings can be placed or when the probability to find a suitable space is very low.

How to get minimum-sized rectangle that contains certain rectangles?

Recently I am thinking of an algorithm that can calculate the minimum-sized rectangle that contains certain rectangles.
The goal of this algorithm is to generate a big rectangle that I can put all given smaller rectangles in. One of these smaller rectangles should not partly or entirely overlap another. And before I put smaller rectangles in the big rectangle, I can rotate them 90 degrees or not. The big rectangle should be as small as it can be.
Does anyone have some clues about it?
I was researching on this issue for some time. And I've come up with a solution reference to some links and articles about this issue. Here are two of the names of articles:,. You can google and download them if you want to read these articles.
It's a pity that none of the articles can exactly solve my problem. Because what I want is neither to check if certain rectangles can be put into a fix-sized large rectangle nor a best area-allocating method that cost long time to result. So I have to balance between time and effect. I do this:
1.I prepare a list of candidate points and add the left-top point to it.
2.I place rectangles one by one into the large rectangle.
3.Every time when I place a rectangle, I check all candidate points to see if I can put the rectangle on the point. And I just keep the best two or three ones(best means minimum-sized if this rectangle is the last one).
4.I choose a candidate point to place the rectangle and replace one point to two points(right-top point and left-bottom one of the rectangle) if they are checked valid to place the next rectangle.
5.I recursively do step 3 to place a next rectangle.
I use C++ language do coding so I can code like this:
void computeMinimunRects(int index, Array objects, Array candidates, RectChecker checker)
{
if (index == objects.count()){
checkresult(objects);// finished putting all rectangles
return;
}
int c = 2;// or 3 which based on objects.count()
SortPoint possibles;// SortPoint sort all input points by total area from small to big.
for (int i = 0; i<candidates.count(); i++){
if (checkput(candidates[i], objects[index].size))// check if objects[index].size can put on candidates[i] point
possibles.insert(candidates[i], objects[index].size);// input all needed value and sort them.
}
for(int i = 0; i<c; i++){
Point pt = possibles[i].pt;
Size size = possibles[i].size;
Rect rect(pt, size);
candidate.remove(pt);// replace point with two ones.
candidate.add(rect.right_top());
candidate.add(rect.left_bottom());
computeMinimunRects(index+1, objects, candidates, checker);// recursively input rectangle
candidates.remove(rect.right_top());// revert all changes
candidates.remove(rect.left_bottom());
candidates.add(pt);
}
}

create a concave polygon from image using N points

i am looking for a algorithm that will generate a concave polygon (with N points where N > 3 - user enters this value) from a image.
My idea for the algorithm:
// Every pixel in image is checked and a minimal orientated bounding box is generated (transparent pixels are ignored)
boundingBox = createImageBoundingBox(image);
curpoints = 4, A = 0, B = 1, tmppoints = curpoints;
while(curpoints < maxNumberOfPoints)
{
add a new point between point A and point B (A and B are points from the boundingBox)
reposition points so that it will contain the minimal surface
A++; B++;
curpoints++;
if(A == tmppoints)
{ A = 0; B = 1; tmppoints=curpoints; }
}
The problem im facing is i dont know how to optimally reposition points. Can this be done any other (better/faster way). Would appreciate any thoughts.
Thanks
EDIT:
The image has to be at least 10x10. I need the N points parameter so the user can regulate how many points are going to be used (for optimization). An alternative would be to have a factor (0-1) which tells how much detailed (how many points) you want the polygon to have (0 being 4 points, > 0 5 or more points). But not sure how to implement it.
You can use a delaunay triangulation and get the average edge lenght. Then try to remove edges that are longer then the average. The concept is from the alpha shapes.
Concave hull may be built with alpha shapes. CGAL link.
1.) Select a point in the middle of the square image.
2.) Jitter this point N times randomly from the center to generate N new points.
3.) Sort these points based on maximum angle from the center point
4.) Use your four points in your bounding box and your midpoint(s) in sorted ascending angle order to create the ordered point list of your concave polygon.
I am not sure if I understand your 'minimal surface' step above, but I believe this algorithm will work for taking a cut out of image to generate a concave polygon. I think this is faster than your above, but I am not sure because I don't understand that step fully.
This will always generate a concave polygon with the same bounds as your original image. If you don't want this, you could add a step 0.) that jitters your bounding box, and then changes your midpoint jitter based on this. Both of these ideas will result in a bounding quadrilateral with a n-sized point chunk taken out, I think.
This requires n > 4 (collapse two of you bounding box points into one if you want this to require n > 3, like you said you want.)

Random Tile layout

I need to place tiles on a large grid radiating from a central point in a way that looks organic and random. New tiles will need to find an open space on the grid that is touching at least 1 other tile.
Can anyone point me in the right to direction to anything that might help with this?
Or some basic concepts I can read up on that are in this vein?
For example, in this picture, there is a shape already created (yellow) and I may be receiving a new tile, that may be 1x1, 2x2, or 3x3. Trying to find a good way to figure out where I can place the new tile so that it will be touching the maximum amount of current tiles.
Picture:
alt text http://osomer.com/grid.JPG
Alternatively, you could approach this problem as the yellow tiles "eroding" away at the blue/background. To do this, at every step, have a yellow tile add a fixed number to the "erosion sum" E of all of the background tiles neighboring it in a cardinal direction (and perhaps maybe a fraction of that to the background tiles neighboring it diagonally).
Then, when it comes time to place a new tile, you can, for each background tile, pick a random number from 0 to E; the greatest one is "eroded" away. Alternatively, you could do a simple weighted random choice, with E being their weights.
For 2x2 or 3x3 tiles, you can pick only from tiles that suitably "fit" a 2x2 or 3x3 square in it (that is, a 2x2 or 3x3 the eroded tile on its edge, so that it doesn't cause overlap with already-placed tiles). But really, you're never going to get something looking as natural as one-by-one erosion/tile placement.
You can save time recalculating erosion sums by having them persist with each iteration, only, when you add a new tile, up the erosion sums of the ones around it (a simple +=). At this point, it is essentially the same as another answer suggested, albeit with a different perspective/philosophy.
A sample grid of Erosion Sums E, with direct cardinal neighbors being +4, and diagonal neighbors being +1:
Erosion Sums http://img199.imageshack.us/img199/4766/erosion.png
The ones with a higher E are most likely to be "eroded" away; for example, in this one, the two little inlets on the west and south faces are most likely to be eroded away by the yellow, followed by the smaller bays on the north and east faces. Least likely are the ones barely touching the yellow by one corner. You can decide which one either by assigning a random number from 0 to E for each tile and eroding the one with the highest random number, or doing a simple weighted random selection, or by any decision method of your choice.
For purely random, you start with an empty grid and a "candidate" list (also empty).
Place the first tile in the centre of the grid, then add each adjacent tile to the one you just placed into the "candidate" list. Then, each turn, choose a random entry in the "candidate" list and place a tile there. Look at each adjancent grid location next to where you just placed the tile, and for each one that is also empty, put it on the "candidate" list for the next time around (if not already there).
To avoid creating holes in your tile grid, increase the probability of selecting a grid location based on the number of adjacent tiles that are already filled (so if only one adjacent tile is already filled, it has low probably. If they're all filled, it'll have a very high probability).
In pseudo code:
grid = new array[width,height];
candidates = new list();
function place_tile(x,y) {
// place the tile at the given location
grid[x,y] = 1;
// loop through all the adjacent grid locations around the one
// we just placed
for(y1 = y - 1; y1 < y + 1; y1++) {
for(x1 = x - 1; x1 < x + 1; x1++) {
// if this location doesn't have a tile and isn't already in
// the candidate list, add it
if (grid[x,y] != 1 && !candidates.contains(x1,y1)) {
candidates.add(x1,y1);
}
}
}
}
// place the first tile in the centre
place_tile(width/2, height/2);
while (!finished) {
// choose a random tile from the candidate list
int index = rand(0, candidates.length - 1);
// place a tile at that location (remove the entry from
// the candidate list)
x, y = candidates[index];
candidates.remove(index);
place_tile(x, y);
}
The problem with your question is that 'organic and random' can be many different things.
Let me show two links
generating random fractal terrain (look at section 'Cloudy Skies' and imagine that you turn it to b/w, or in your case yellow/background).
simulating erosion (look at the image under 'erode')
The two above samples are 'organic and random' to me, but you might not be satisfied with those. So, I think you will have to better define what is 'organic and random'.
For now, I'll take your definition of the guiding rule for adding new tiles (but don't think it is necessarily the same problem), which I read as:
Given two shapes (assuming bitmaps)
find the relative position of the
shapes such that the number of
touching sides is maximum
I will also assume
overlap is not allowed
you can leave holes inside the resulting, merged shape
you can not rotate shapes
Under such conditions you need to test less then xy solutions and in each you need to
- discard it if there is an overlap
- discard it if they do not touch
- if they touch then count the number of edges that are common
All three of the above tests can be done in constant time by scanning all the yellow tiles (number of which is konstx*y)
So, the above can be easily done in O(n^4), is that good enough for you?
Compute a random spanning tree for the dual graph, that is, the grid whose vertices are the centers of your cells. For that, start at the center of the grid and do a random depth-first search. Then plot cells fro increasing tree distance from the center.

Resources