This procedure finds all the points in a given Polygon and disperses them in a pattern similar to the esri tool arcpy.DisperseMarkers_Management with "SQUARES" option.
/*
This procedure finds all the points in a given Polygon and disperses them in a pattern similar to the
esri tool arcpy.DisperseMarkers_Management with "SQUARES" option.
Author: @MapLion for @Simplot
*/
CREATE Procedure Disperse_Points
@Shape GEOMETRY,
@Points_Table_Name nvarchar(255),
@Points_Schema_Name nvarchar(100) = '',
@Points_Database_Name nvarchar(100) = '',
@Dispersion_Type nvarchar(15) = 'SQUARES'
as
BEGIN
SET NOCOUNT ON;
/* Drop existing Temporary Objects */
IF OBJECT_ID('tempdb..#Points_Table') IS NOT NULL DROP TABLE #Points_Table
IF OBJECT_ID('tempdb..#Shape_Table') IS NOT NULL DROP TABLE #Shape_Table
/* Set @Points_Table_Name */
IF @Points_Database_Name <> ''
SET @Points_Database_Name = CONCAT(@Points_Database_Name, '.')
IF @Points_Schema_Name <> ''
SET @Points_Schema_Name = CONCAT(@Points_Schema_Name, '.')
SET @Points_Table_Name = CONCAT(@Points_Database_Name, @Points_Schema_Name, @Points_Table_Name)
--select @Points_Table_Name
/* Declare Dynamic SQL Variables */
DECLARE @SQL nvarchar(MAX)
DECLARE @DEBUG INT = 0 -- SET to 1 to print out all Dynamic SQL Statements or wrap specific statements with SET @DEBUG = 1 and SET @DEBUG = 0
/* POINT DISPERSION */
-----------------------------------------------------------------------------------------
/* Get a list of all points within Shape along with their X and Y Coordinates */
Create Table #Points_Table (
OBJECTID INT
, Shape GEOMETRY
, X_Coord FLOAT
, Y_Coord FLOAT
)
/* This table is created to hold a single geometry @Shape for the purpose of being able to use it in dynamic SQL */
Create Table #Shape_Table (
Shape GEOMETRY
)
/* Populate @Shape into #Shape_Table */
insert into #Shape_Table
select @Shape
/* Populate Temporary Points Table */
SET @SQL =
'insert into #Points_Table
select
OBJECTID
, Shape
, Shape.STX X_Coord
, Shape.STY Y_Coord
from ' + @Points_Table_Name + ' p
where (select TOP 1 Shape from #Shape_Table).STContains(p.Shape) = 1'
IF @DEBUG = 1 PRINT '1.' + CHAR(13) + @SQL + CHAR(13)
EXECUTE sp_executesql @SQL WITH RESULT SETS NONE
/* Get Centroid X and Y */
DECLARE @CENTROID_X FLOAT
DECLARE @CENTROID_Y FLOAT
select @CENTROID_X = @Shape.STCentroid().STX
select @CENTROID_Y = @Shape.STCentroid().STY
/* Get Count of Points */
DECLARE @POINT_COUNT INT
select @POINT_COUNT = count(*) from #Points_Table
/* Set Base Square Value */
DECLARE @BASE_SQUARE_VALUE FLOAT = 0.0001325 /* This is an arbitrary measure based on evaluations of esri's DisperseMarkers_Management results */
/* Reset Points to Centroid */
update #Points_Table
set X_Coord = @CENTROID_X, Y_Coord = @CENTROID_Y
/* Change Coordinate Values */
BEGIN TRY
BEGIN TRAN
DECLARE @OBJECTID INT
DECLARE @MOVED_POINT GEOMETRY
DECLARE @COUNT INT = 0
/* SQUARES DISPERSION */
--------------------------------------------------
IF @Dispersion_Type = 'SQUARES'
BEGIN
IF @POINT_COUNT <= 4
BEGIN
select @OBJECTID = min(OBJECTID) from #Points_Table
WHILE @OBJECTID IS NOT NULL
BEGIN
SET @COUNT = @COUNT + 1
IF @COUNT = 1
BEGIN
update #Points_Table
set X_Coord = X_Coord - @BASE_SQUARE_VALUE, Y_Coord = Y_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 2
BEGIN
update #Points_Table
set X_Coord = X_Coord + @BASE_SQUARE_VALUE, Y_Coord = Y_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 3
BEGIN
update #Points_Table
set X_Coord = X_Coord + @BASE_SQUARE_VALUE, Y_Coord = Y_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 4
BEGIN
update #Points_Table
set X_Coord = X_Coord - @BASE_SQUARE_VALUE, Y_Coord = Y_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
/* Convert New X Y to a shape */
select @MOVED_POINT = geometry::Point(X_Coord, Y_Coord, 4326)
from #Points_Table
where OBJECTID = @OBJECTID
/* Update table with new shape */
update #Points_Table
set Shape = @MOVED_POINT
where OBJECTID = @OBJECTID
/* Grab next OBJECTID */
select @OBJECTID = min(OBJECTID) from #Points_Table where OBJECTID > @OBJECTID
END /* END WHILE @OBJECTID IS NOT NULL */
END /* END IF @POINT_COUNT <= 4 */
ELSE /* More than 4 labels */
BEGIN
select @OBJECTID = min(OBJECTID) from #Points_Table
WHILE @OBJECTID IS NOT NULL
BEGIN
IF (@COUNT % 8) = 0
BEGIN
SET @BASE_SQUARE_VALUE = @BASE_SQUARE_VALUE * 2
SET @COUNT = 0
END
SET @COUNT = @COUNT + 1
IF @COUNT = 1
BEGIN
update #Points_Table
set X_Coord = X_Coord - @BASE_SQUARE_VALUE, Y_Coord = Y_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 2
BEGIN
update #Points_Table
set Y_Coord = Y_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 3
BEGIN
update #Points_Table
set X_Coord = X_Coord + @BASE_SQUARE_VALUE, Y_Coord = Y_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 4
BEGIN
update #Points_Table
set X_Coord = X_Coord + @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 5
BEGIN
update #Points_Table
set X_Coord = X_Coord + @BASE_SQUARE_VALUE, Y_Coord = Y_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 6
BEGIN
update #Points_Table
set Y_Coord = Y_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 7
BEGIN
update #Points_Table
set X_Coord = X_Coord - @BASE_SQUARE_VALUE, Y_Coord = Y_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
ELSE IF @COUNT = 8
BEGIN
update #Points_Table
set X_Coord = X_Coord - @BASE_SQUARE_VALUE
where OBJECTID = @OBJECTID
END
/* Convert New X Y to a shape */
select @MOVED_POINT = geometry::Point(X_Coord, Y_Coord, 4326)
from #Points_Table
where OBJECTID = @OBJECTID
/* Update table with new shape */
update #Points_Table
set Shape = @MOVED_POINT
where OBJECTID = @OBJECTID
/* Grab next OBJECTID */
select @OBJECTID = min(OBJECTID) from #Points_Table where OBJECTID > @OBJECTID
END /* END WHILE @OBJECTID IS NOT NULL */
END /* End ELSE */
END /* END IF @Dispersion_Type = 'SQUARES' */
--------------------------------------------------
/* END SQUARES DISPERSION */
/* Boundary Check */
IF (select COUNT(*) CNT from (select * from #Points_Table where @Shape.STContains(Shape) = 0) a) > 0
BEGIN
;THROW 50005, 'Markers are outside of the range of the Polygon; there are too many points for the current implementation with the given shape.', 1;
END
--select * from #Points_Table
--select geometry::Point(X_Coord, Y_Coord, 4326) NewShape from #Points_Table
/* Update Original Points Table with dispersed points */
SET @SQL =
'update i
set i.Shape = p.Shape
from #Points_Table p
inner join ' + @Points_Table_Name + ' i
on i.OBJECTID = p.OBJECTID'
IF @DEBUG = 1 PRINT '1.' + CHAR(13) + @SQL + CHAR(13)
EXECUTE sp_executesql @SQL WITH RESULT SETS NONE
COMMIT TRAN;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRAN;
DECLARE @ERROR_SEVERITY INT = ERROR_SEVERITY()
DECLARE @ERROR_STATE INT = ERROR_STATE()
DECLARE @ERROR_MESSAGE_CONCAT NVARCHAR(MAX) = CONCAT(
char(13),'Error Procedure: ', ERROR_PROCEDURE(),
char(13),'Error Number: ',ERROR_NUMBER(),
char(13),'Error Line: ', ERROR_LINE(),
char(13),'Error Message: ', ERROR_MESSAGE())
RAISERROR(@ERROR_MESSAGE_CONCAT, @ERROR_SEVERITY, @ERROR_STATE)
END CATCH;
-----------------------------------------------------------------------------------------
/* END POINT DISPERSION */
END