This is not an efficient answer but it is fun to play with so I thought I'd post it. For efficiency the use of `Nearest` might provide a good starting point.

 g[n_, {low_, high_}, minDist_, step_: 1] := 
 Block[{data = RandomReal[{low, high}, {n, 2}], temp, happy, sdata, 
 hdata},
 
 While[True,
 temp = ((Nearest[data][#, 2][[-1]] & /@ data));
 happy = 
 Boole@Thread[MapThread[EuclideanDistance, {temp, data}] > minDist];
 hdata = Pick[data, happy, 1];
 sdata = Pick[data, happy, 0];
 If[sdata === {}, Break[]];
 sdata += RandomReal[{-step, step}, {Length[sdata], 2}];
 data = Join[sdata, hdata];
 ];
 data
 ]

The idea is to do an initial sampling and then allow the points that are too close to "walk" somewhere else. The function takes a desired number of points `n`, a low and high value for the data range, the minimum acceptable distance between points `minDist` and a `step` argument which allows points to "walk" up to a certain distance in the x and y directions.

Its especially fun to watch dynamically.

 g[150, {0, 30}, 1.5, 1]

![enter image description here][1]


 [1]: https://i.sstatic.net/NsuiM.gif