8

I have a shapefile of points representing households in a household survey.

In order to conceal their exact geographic location, I would like to displace these points in a random distance (uniform distribution over 0-2km) and a random direction (uniform distribution over 0-360 degrees).

How can I do this in QGIS?

Although displacing on the x- and y-axis independently creates a random direction, displacing by 2000m on the x and 2000m on the y would lead to more than a 2000m displacement in total. I thought about displacing up to 1414.2135m on both axes, but I wasn't convinced this would generate a uniform distribution over distance (I could be wrong, but I thought it would more likely create smaller distances).

The workaround I came up with was to create a random degree using the field editor rand(0,90) and a random quadrant using rand(1,4). Then I created a random distance using rand(0,2000). Let x* be the displaced x-coordinate of original point; similarly, y* is the displaced y-coordinate of the original point.

In quadrant I : x*=x+distance*sin(degree) and y*=y+distance*sin(90-degree)
In quadrant II : x*=x-distance*sin(90-degree) and y*=y+distance*sin(degree)
In quadrant III : x*=x-distance*sin(degree) and y*=y-distance*sin(90-degree)
In quadrant IV : x*=x+distance*sin(90-degree) and y*=y-distance*sin(degree)

1
  • 1
    Another solution would be to create a 2 km buffer around the points, and use the random points in polygon tool to generate one random point per buffer polygon. Much simpler than calculating random angles.
    – csk
    Commented Oct 24, 2019 at 15:21

4 Answers 4

4

Use "Geometry generator"

Actually, QGIS provides a much easier way to do this: Geometry generator.

No need to create any new layer, nor any new calculated field, just add a Geometry generator marker in the Symbology pane, with soemthing similar to this content:

make_point(
  $x + rand(-2000,2000),
  $y + rand(-2000,2000)
  )

or, to get a true 2km radius:

with_variable('randang', rand(0,360),
with_variable('randdist', rand(0,2000),
make_point(
  $x+@randdist*cos(@randang),
  $y+@randdist*sin(@randang)
  )
  ))

enter image description here

  • yellow dots are the original, actual points
  • red dots are the randomized ones.

You can of course remove the representation of the actual points, and leave only the randomized one, which addresses your question:

enter image description here

Checking the correctness

You can also check the correctness of this approach by drawing displacement lines instead of displaced points:

enter image description here

This is accomplished selecting LineString / MultiLineString as Geometry type and with the following:

with_variable('randang', rand(0,360),
with_variable('randdist', rand(0,2000),
make_line(
    make_point($x+@randdist*cos(@randang),$y+@randdist*sin(@randang)),
    make_point($x,$y)
  )
))
3
  • Dear @RafDouglas, I'm trying to follow the method you described, but cant get stable random points. After setting up displacement parameters in Expression Dialog and pressing OK, I see only original points on the same places. But if I use Identify Features instrument I can see contours of displaced points which change their position with every clic. Seems like these points has no stable lat long coordinates and can't be rendered as you described. What can cause such an effect? I'm using QGIS 3.10, Win 7/64.
    – Burtsev
    Commented May 12, 2020 at 12:22
  • 1
    Thanks for a great reply. Trying to adapt it to my case (in long/lat coordinates), it turned out that I needed to use randf() rather than rand() [randf() returns a float]. Also, I wanted to jitter the points by a data-defined value. So, applied to my case, the final geometry generator reads
    – jfmoyen
    Commented Jun 2, 2020 at 4:39
  • 1
    make_point( $x+rand(-"accuracy","accuracy"), $y+rand(-"accuracy","accuracy") )
    – jfmoyen
    Commented Jun 2, 2020 at 4:40
4

Alternatively, I can suggest using a "Virtual Layer" through Layer > Add Layer > Add/Edit Virtual Layer...

Let's assume there is a point layer called "points".

input_1

With the following query, it is possible to achieve the result where each point will be shifted on a certain delta generated randomly.

SELECT id,
       name,
       make_point(st_x(geometry)+random() % 100,st_y(geometry)+random() % 100) AS geom,
       random() % 100 AS "delta_x",
       random() % 100 AS "delta_y"
FROM "points"

The output Virtual Layer with its corresponding attribute table will look as following

output


If the point layer "points" attached to a polygon layer "polygons" (see image below), basically it means, that a new point has to be generated inside a corresponding polygon.

input_2

For this purpose use the following query

SELECT poi.id,
       point_on_surface(p.geometry) AS geom,
       round(st_x(poi.geometry)-st_x(point_on_surface(p.geometry)),4) AS "delta_x",
       round(st_y(poi.geometry)-st_y(point_on_surface(p.geometry)),4) AS "delta_y"
FROM "polygons" AS p
JOIN "points" AS poi ON st_within(poi.geometry, p.geometry)

The query above contains the ST_PointOnSurface() function.

result

Note: That this solution is working only for 1:1 relation between a point feature and a polygon feature within which the point lays.


References:

1
  • This method doesn't work if original file is GeoJSON. After conversion to SHP all works fine.
    – Burtsev
    Commented May 12, 2020 at 12:43
2

I had never done this before but I think I that you can give my solution a try. What I did was to add two new X and Y fields to my shapefile (my layer projected in UTM18N, which is meters). Then, I used the field calculator to obtain the UTM coordinates of each point but adding to it a random value between 100 and 200. I input $x+(rand(100,200)) and $y+(rand(100,200)).

You export this layer as CSV and then reload it to QGIS as a delimited text file using these new X and Y fields. Your points will be randomly displaced. I tried it out in QGIS and it works; see the image below.

Edit: you can try some thing like $y+(rand(rand(-200,-100),rand(100,200))) if you want more dispersion. I still haven't figure out how to do the 0-360 degrees part.

I hope it helps.

enter image description here

2
  • Thank you for your comment. Please see my edit to the original post to see if my proposed solution for degree makes sense
    – rachel
    Commented Oct 24, 2019 at 15:18
  • Instead of using the field calculator to calculate new coordinates as attributes, then exporting and re-importing, try using the geometry by expression tool to generate new geometries in one step. Make a point out of your randomly displaced X and Y coordinates like this: make_point($x+(rand(-2000,2000)), $y+(rand(-2000,2000)). Using -2000 and 2000 as the offset, in a CRS that uses meters, will give you a displacement of up to 2 km in any direction.
    – csk
    Commented Oct 24, 2019 at 15:18
1

The answer by @1buzz seems complete to me in terms of achieving movement in both random distance and random direction (rotation if you will). Since the x and y random displacements are independent of each other, then this accomplishes a shift in a random direction as well as a random distance from the source point, within the constraints imposed. However, you need to cater for negative displacements as well as positives, so what about $x = $x+randf(-2000,2000) and same for y, which should open up the randomized space to a square centred on the original point (as I understand it, randf returns a floating number rather than an integer). You might decide the integer returned by "rand" is sufficient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.