SQL Server Spatial & ArcSDE Chris Ebright - Whiting Oil & Gas Corporation Whiting GIS Environment • SQL Server 2008 R2 • SQL Server Management Studio 10.50.2500.0 • ArcGIS Server 10.0 • ArcSDE 10.0 • ArcGIS Desktop 10.0 • Python 2.6 (additional modules from standard library) Why Use SQL Server Spatial Datatypes? 1) Existing Processing on large datasets too slow • • • Previous processing done with Toolbox and/or Python Datasets being analized growing, cannot run all processes overnight SQL Server procedures can be much faster if used correctly 2) 3rd party access to spatial data • • • Infrastructure of multiple SQL Server Databases Existing reports could access spatial data within existing procedures Flexibility for future data access from unknown sources 3) Whiting in-house expertise in SQL Server • • Leverage in-house experience in Query optimization Backup; Procedures are stored on server and can be understood by multiple employees. I am only Python programmer, no backup Challenges of SQL Server Spatial Datatypes • Data Management is more complicated • No re-projection support with out of the box tools (SQL Server queries only, ArcGIS will still handle re-projections) • Must manage OBJECTIDs if Inserting data • May require a new set of skills to interact in SQL Server environment if no SQL Server experience Creating Feature Class Using SQL Server Geometry ArcCatalog– Configuration Keyword Only one step different than default feature class creation Drop down options will vary Based on which RDBMS is installed (SQL Server, Oracle, PostGIS, etc.) GEOMETRY vs. GEOGRAPHY GEOMETRY • Cartesian based • Can store Z and M values, but SQL Spatial functions do not use • ArcGIS will handle the Z and M values • Can be cast to GEOGRAPHY for crucial calculations (large features) • More ST functions GEOGRAPHY • Ellipsoidal based • Can store Z and M values, but SQL Spatial functions do not use • ArcGIS will NOT handle Z and M values (will error on feature class creation) Creating Feature Class Using SQL Server SDE Binary SQL Server MGMT Studio – Business Table Query Results Creating Feature Class Using SQL Server SDE Binary SQL Server MGMT Studio – SDE Geometry Lookup LINESTRING_SDE_BINARY joins to f1203 to get spatial data Creating Feature Class Using SQL Server SDE Binary SQL Server MGMT Studio – SDE Feature Table Query Results LINESTRING_SDE_BINARY joins to f1203 to get spatial data Creating Feature Class Using SQL Server Geometry SQL Server MGMT Studio – Query Results LINESTRING_GEOMETRY has spatial data in business table Creating Feature Class Using SQL Server Geometry SQL Server MGMT Studio - Spatial Results Tab SQL Server MGMT Studio shows spatial results for SQL Geometries Editing Feature Class Using SQL Server Geometry ArcMap - Editing Notes on Data Management in SQL Server Lessons Learned Along the Way • If Adding data to feature class, must manage OBJECTIDs • Must update feature class extents in sde_layers table • Layer will plot in ArcMap, but may not display in Map Service • Must update sde_column_registry table if altering table schema in SQL Server • Recommend doing schema updates through ArcGIS interface What has been tested Tested • Editing in ArcMap with no additional versions • Adding/Updating features from SQL Server side • Creating feature class in ArcGIS first, using configuration keyword Not Tested • Editing in ArcMap with multiple versions • Advanced geodatabase functionality: Topology, Networks, etc. • Creating table in SQL Server first and registering with SDE command line SQL Server Spatial Queries Examples Example: Non ArcGIS spatial data access Scenario: Users managing wellbore paths in ArcMap. Other users use spatial attributes in SQL Server. SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries Wellbores – PolylineZM Feature Class stored in SQL Server GEOMETRY 3D View SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE ,SHAPE.STAsText() AS LINESTRING_TEXT FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' OGC Well Known Text POINT WKT: POINT(-100.5 44.1) GEOMETRY: STGeomFromText( ‘POINT(-100.5 44.1)’, 4267 ) LINESTRING WKT: LINESTRING(-100.5 44.1, -100.6 44.05, -100.7 44.0) GEOMETRY: STGeomFromText( ‘LINESTRING(-100.5 44.1 , -100.6 44.05, -100.7 44.0)’, 4267 ) POLYGON WKT: POLYGON((-100.5 44.1, -100.0 44.1, -100.0 43.8, -100.5 43.8, -100.5 44.1)) GEOMETRY: STGeomFromText( ‘POLYGON((-100.5 44.1, -100.0 44.1, -100.0 43.8, -100.5 43.8, -100.5 44.1))’, 4267 ) SQL Server 2008 R2 OGC Methods (GEOMETRY) STArea STDisjoint STIsClosed STPointOnSurface STAsBinary STDistance STIsEmpty STRelate STAsText STEndpoint STIsRing STSrid STBoundary STEnvelope STIsSimple STStartPoint STBuffer STEquals STIsValid STSymDifference STCentroid STExteriorRing STLength STTouches STContains STGeometryN STNumGeometries STUnion STConvexHull STGeometryType STNumInteriorRing STWithin STCrosses STInteriorRingN STNumPoints STX STDifference STIntersection STOverlaps STY STDimension STIntersects STPointN SQL Server 2008 R2 Extended Methods AsGml (geometry Data Type) MakeValid (geometry Data Type) AsTextZM (geometry Data Type) Reduce (geometry Data Type) BufferWithTolerance (geometry Data Type) ToString (geometry Data Type) InstanceOf (geometry Data Type) Z (geometry Data Type) Filter (geometry Data Type) IsNull (geometry Data Type) M (geometry Data Type) SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE.AsTextZM() AS LINESTRING_TEXT_ZM FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' Z and M Values SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE ,SHAPE.STLength() AS Length ,SHAPE.STNumPoints() AS Num_Points ,SHAPE.STStartPoint() AS SHL ,SHAPE.STEndPoint() AS BHL FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' Native Unit of Geometry (Degrees) (only 2D length, Z value not used) Binary Represenation of Geometry SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE ,CAST(geography::STGeomFromText(SHAPE.STAsText(),SHAPE.STSrid).STLength() * 3.28084 AS INT) AS Length_Feet ,SHAPE.STNumPoints() AS Num_Points ,SHAPE.STStartPoint().STX AS SHL_LON ,SHAPE.STStartPoint().STY AS SHL_LAT ,SHAPE.STEndPoint().STX AS BHL_LON ,SHAPE.STEndPoint().STY AS BHL_LAT FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' Conversion to feet using Using GEOGRAPHY datatype Float represenation of coordinates SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT UWI ,SHAPE ,CAST(geography::STGeomFromText(SHAPE.STAsText(),SHAPE.STSrid).STLength() * 3.28084 AS INT) AS Length_Feet ,SHAPE.STEndPoint().M AS Total_Depth ,CAST(SHAPE.STEndPoint().Z AS INT) AS TVDSS_at_TD ,SHAPE.STNumPoints() AS Num_Points FROM database.dbo.PATHS WHERE UWI LIKE '3306100513%' M value storing MD value Z value storing TVDSS value Length Calc without Z input SQL Server Spatial Queries Examples – SQL Server MGMT Studio Queries SELECT t1.[UWI] ,t1.SHAPE.STAsText() AS WellBore_Geometry ,t2.SHAPE.STAsText() AS Basin_Geometry ,t2.Name AS Basin_Name FROM database.dbo.PATHS AS t1 JOIN database.dbo.BASINS AS t2 ON t1.SHAPE.STIntersects(t2.SHAPE) = 1 WHERE UWI LIKE '3306100513%' Attribute of Intersected Geometry SQL Server Spatial Queries Examples – Simple Well Paths Example: Simple wellbore path from SHL and BHL Python vs. SQL Server SQL Server Spatial Queries Examples – Simple Well Paths Python Script ~336,000 wells in 48 min. import myLogger import urllib2 import datetime import time import arcpy import sys import os import math #Create Insert cursor on destination table InsertCursor = arcpy.InsertCursor(fc_well_paths) ic_row = InsertCursor.newRow() pointSHL = arcpy.Point() pointBHL = arcpy.Point() pointArray = arcpy.Array() fileAbsolutePath = os.path.abspath(__file__) try: for row in SearchCursor: try: ic_row.setValue('UWI', row.UWI) myLog = myLogger.Log(fileAbsolutePath) myLog.reportStart() arcpy.env.overwriteOutput = 1 if row.WELL_NAME == None: #Set workspace arcpy.env.workspace = r'Database Connections\server.database.sde' env = arcpy.env.workspace ic_row.setValue('WELL_NAME', '') elif row.WELL_NUMBER == None: fc_well_paths = r'Database Connections\server.database.sde\database.DBO.WELL_PATHS' if row.WELL_NAME == None: #Check if feature already exists, and if so, delete if arcpy.Exists(fc_well_paths): print "exists" #arcpy.Delete_management(fc) arcpy.DeleteFeatures_management(fc_well_paths) else: print "doesn't exist" #Create spatial reference sr = arcpy.SpatialReference() sr.factoryCode = 4267 arcpy Cursors and Loops SLOWER #Create feature class based on template arcpy.CreateFeatureclass_management(env, fc_well_paths, 'POLYLINE', '', '', '', sr) if len(arcpy.Describe(fc_well_paths).fields) > 2: pass else: arcpy.AddField_management(fc_well_paths, 'UWI', 'text') arcpy.AddField_management(fc_well_paths, 'WELL_NAME', 'text') ic_row.setValue('WELL_NAME', '') else: ic_row.setValue('WELL_NAME', row.WELL_NAME) else: ic_row.setValue('WELL_NAME', str(row.WELL_NAME) + ' ' + str(row.WELL_NUMBER)) pointArray.removeAll() pointSHL.X = row.getValue('SURFACE_LONGITUDE') pointSHL.Y = row.getValue('SURFACE_LATITUDE') pointBHL.X = row.getValue('BOTTOM_HOLE_LONGITUDE') pointBHL.Y = row.getValue('BOTTOM_HOLE_LATITUDE') pointArray.add(pointSHL) pointArray.add(pointBHL) newLine = arcpy.Polyline(pointArray) ic_row.setValue('SHAPE', newLine) InsertCursor.insertRow(ic_row) #Create Search cursor on source table SearchCursor = arcpy.SearchCursor(r'Database Connections\server.database2sde\database2.dbo.WELLS_SURF', \ 'BOTTOM_HOLE_LATITUDE IS NOT NULL AND \ BOTTOM_HOLE_LONGITUDE IS NOT NULL AND \ BOTTOM_HOLE_LONGITUDE <> 0 AND \ BOTTOM_HOLE_LATITUDE <> 0 AND \ SURFACE_LONGITUDE <> 0 AND \ SURFACE_LATITUDE <> 0 AND \ BOTTOM_HOLE_LATITUDE <> SURFACE_LATITUDE AND \ BOTTOM_HOLE_LONGITUDE <> SURFACE_LONGITUDE', '', '', '') except Exception, e: myLog.reportError(e) del InsertCursor, ic_row, pointSHL, pointBHL, pointArray except Exception, e: myLog.reportError(e) myLog.reportToDatabase('FALSE', e) #sys.exit() SQL Server Script ~336,000 wells in 2 min. DELETE FROM database.dbo.PATHS_SIMPLE INSERT INTO database.dbo.PATHS_SIMPLE ( OBJECTID ,SHAPE ,WELL_NAME ) ( SELECT ROW_NUMBER() OVER(ORDER BY COMPLETION_DATE) ,geometry::STGeomFromText( 'LINESTRING(' + STR(SURFACE_LONGITUDE, 25, 25) + ' ' + STR(SURFACE_LATITUDE, 25, 25) + ',' + STR(BOTTOM_HOLE_LONGITUDE, 25, 25) + ' ' + STR(BOTTOM_HOLE_LATITUDE, 25, 25) + ')' , 4267) ,WELL_NAME + ' ' + WELL_NUM FROM database2.dbo.WELL WHERE BOTTOM_HOLE_LATITUDE IS NOT NULL AND BOTTOM_HOLE_LONGITUDE IS NOT NULL AND BOTTOM_HOLE_LONGITUDE <> 0 AND BOTTOM_HOLE_LATITUDE <> 0 AND SURFACE_LONGITUDE <> 0 AND SURFACE_LATITUDE <> 0 AND BOTTOM_HOLE_LATITUDE <> SURFACE_LATITUDE AND BOTTOM_HOLE_LONGITUDE <> SURFACE_LONGITUDE ) Set based SQL Query FASTER Python Integration Code Snippets import pyodbc Open Connection to SQL Server table conn = pyodbc.connect('DRIVER={SQL Server};SERVER=<server>;DATABASE=<database>') c = conn.cursor() Execute SELECT statement on table cursor.execute('SELECT MAX(OBJECTID) FROM featureClass') oid = cursor.fetchall()[0][0] Execute SELECT statement on table cursor.execute('SELECT COUNT(*) FROM featureClass WHERE ID = ?', var_id) count = cursor.fetchall()[0][0] Execute INSERT statement on table sql = "INSERT INTO featureClass(OBJECTID, SHAPE) (SELECT %d, %s)" % (oid, geometry) cursor.execute(sql) conn.commit() Execute UPDATE statement on table cursor.execute("UPDATE featureClass SET ID = oid + '_' + subID") conn.commit() SQL Server Spatial Queries Examples – Spatial Analysis for Wellbores in Fields SQL Server Spatial Queries Examples – View From Spatial Query SELECT t1.UWI ,t1.SHAPE.STStartPoint().STY AS SHL_LAT ,t1.SHAPE.STStartPoint().STX AS SHL_LON ,t1.SHAPE.STEndPoint().STY AS BHL_LAT ,t1.SHAPE.STEndPoint().STX AS BHL_LON ,t1.SHAPE.STEndPoint().M AS TD ,t1.SHAPE.STEndPoint().Z AS TVDSS_TD ,t2.name AS Basin ,t3.name AS Field FROM database.dbo.PATHS AS t1 JOIN database.dbo.BASINS AS t2 ON t1.SHAPE.STIntersects(t2.SHAPE) = 1 JOIN database.dbo.FIELDS AS t3 ON t1.SHAPE.STIntersects(t3.SHAPE) = 1 WHERE t1.UWI LIKE '%33061%' Dynamic Intersect of wellbores and field outlines SQL Server Spatial Queries Examples – Excel Linking to SQL Server view UWI 33061012810000 33061012830000 33061012840000 33061012850000 33061012860000 33061012890000 33061012890000 33061012890100 33061012890100 33061012910000 33061012910000 33061012910100 33061012910100 33061012930000 33061012940100 33061012950000 33061012950100 33061012950200 33061012950300 33061012960000 33061012970000 33061012980000 33061012990000 33061013000000 33061013010000 33061013030000 33061013030000 33061013030100 SHL_LAT SHL_LON BHL_LAT BHL_LON TD 47.8469638 -102.4013162 47.82176924 -102.4007689 48.17510063 -102.5050341 48.18074926 -102.5465628 48.07808842 -102.5236354 48.05379697 -102.519398 48.29890103 -102.5051834 48.3111511 -102.5189636 48.09465782 -102.2814816 48.08296043 -102.2693611 48.40238924 -102.8263396 48.42956384 -102.8312775 48.40238924 -102.8263396 48.42956384 -102.8312775 48.40238924 -102.8263396 48.42956835 -102.8312966 48.40238924 -102.8263396 48.42956835 -102.8312966 48.40239041 -102.8267512 48.42956577 -102.8381938 48.40239041 -102.8267512 48.42956577 -102.8381938 48.40239041 -102.8267512 48.42954232 -102.838462 48.40239041 -102.8267512 48.42954232 -102.838462 48.28440775 -102.559069 48.25537918 -102.5579144 47.93351284 -102.4788547 47.92141017 -102.4747555 47.81787922 -102.4656598 47.81633447 -102.4656387 47.81787922 -102.4656598 47.80098009 -102.4639435 47.81787922 -102.4656598 47.80216198 -102.4640192 47.81787922 -102.4656598 47.79109206 -102.4631314 47.89002705 -102.2352256 47.87799745 -102.226253 48.10859343 -102.436319 48.10544051 -102.3973608 48.03827066 -102.2980918 48.05028143 -102.3075429 48.0391278 -102.2895798 48.06365464 -102.3074976 48.32780537 -102.483963 48.34013288 -102.4975229 47.94764525 -102.2759062 47.9619009 -102.2944982 47.85867453 -102.2737543 47.85765902 -102.3034023 47.85867453 -102.2737543 47.85765902 -102.3034023 47.85867453 -102.2737543 47.85785747 -102.3112671 17213 18015 9316 12899 12505 19185 19185 19158 19158 20124 20124 20118 20118 17992 12784 8740 14365 13933 17993 12302 17311 12435 17363 12746 13257 16063 16063 18009 TVDSS_TD Basin -6424.591983 WILLISTON BASIN -5695.602066 WILLISTON BASIN 1731.908387 WILLISTON BASIN -5291.967185 WILLISTON BASIN -5203.131073 WILLISTON BASIN -7413.212465 WILLISTON BASIN -7413.212465 WILLISTON BASIN -7307.849413 WILLISTON BASIN -7307.849413 WILLISTON BASIN -7387.343073 WILLISTON BASIN -7387.343073 WILLISTON BASIN -7289.199102 WILLISTON BASIN -7289.199102 WILLISTON BASIN -5637.707683 WILLISTON BASIN -6638.841291 WILLISTON BASIN -6514.837079 WILLISTON BASIN -6442.769883 WILLISTON BASIN -6444.988622 WILLISTON BASIN -6410.967479 WILLISTON BASIN -5674.628805 WILLISTON BASIN -5778.43068 WILLISTON BASIN -5338.195557 WILLISTON BASIN -5497.51321 WILLISTON BASIN -5228.140241 WILLISTON BASIN -5369.170037 WILLISTON BASIN -7854.478002 WILLISTON BASIN -7854.478002 WILLISTON BASIN -7905.065345 WILLISTON BASIN Dynamic link to SQL Server View Updates on feature class update Field VAN HOOK SANISH SANISH ALGER PARSHALL WHITE EARTH PLEASANT VALLEY WHITE EARTH PLEASANT VALLEY WHITE EARTH PLEASANT VALLEY WHITE EARTH PLEASANT VALLEY ALGER BIG BEND VAN HOOK VAN HOOK VAN HOOK VAN HOOK PARSHALL SANISH PARSHALL PARSHALL ALGER PARSHALL PARSHALL VAN HOOK PARSHALL SQL Server Spatial Queries Examples – View From Spatial Query SELECT t2.name AS Field ,COUNT(t1.SHAPE) AS Total_Wellbores ,MAX(CAST(geography::STGeomFromText(t1.SHAPE.STAsText(),t1.SHAPE.STSrid).STLength() * 3.28084 AS INT) ) AS Longest_Wellbore ,AVG(CAST(geography::STGeomFromText(t1.SHAPE.STAsText(),t1.SHAPE.STSrid).STLength() * 3.28084 AS INT) ) AS Avg_Wellbore ,SUM(t1.SHAPE.STEndPoint().M) AS Total_Feet_Drilled FROM database.dbo.PATHS AS t1 JOIN database.dbo. FIELDS AS t2 ON t1.SHAPE.STIntersects(t2.SHAPE) = 1 WHERE t1.UWI LIKE '%33061%' GROUP BY t2.name ORDER BY Avg_Wellbore DESC SQL Server Spatial Queries Examples – Excel Linking to SQL Server view Field Total_Wellbores Longest_Wellbore Avg_Wellbore Total_Feet_Drilled PLEASANT VALLEY 15 11221 9713 80508 SORKNESS 10 10591 9709 154366 BIG BUTTE 64 11744 9695 746954 ALKALI CREEK 52 12260 9514 782042 REUNION BAY 70 13653 9419 1125619 WHITE EARTH 11 11221 9378 115260 MANITOU 55 11694 9029 819613 ROBINSON LAKE 70 11906 8857 914325 ROSS 101 12319 8747 1047145 ALGER 210 13182 8657 2772412 KITTLESON SLOUGH 38 10747 8511 428223 SANISH 534 12777 8408 6280677 BASKIN 7 11732 7946 111073 COTTONWOOD 45 10590 7634 392902 EAST TIOGA 16 10734 7505 191527 CLEAR WATER 80 11237 7356 811861 125 15003 7319 1405354 8 10905 7137 111020 94 12319 7122 855890 2 11383 6636 30124 BIG BEND 54 11076 6404 704987 PARSHALL 319 13442 6351 3063418 2 10338 6058 30574 TIOGA 27 11081 2840 185300 PLAZA 1 2280 2280 8479 WABEK 4 1939 1649 21383 COULEE 1 78 78 VAN HOOK ENGET LAKE STANLEY WHITE LAKE POWERS LAKE Dynamic link to SQL Server View Updates on feature class update Find Psuedo Midpoint of Linestring Update Procedure ;WITH PATHS_CTE(rownumber ,UWI ,ROW_CHANGED_DATE ,midpoint ) AS ( SELECT ROW_NUMBER() OVER(ORDER BY UWI) ,[UWI] ,ROW_CHANGED_DATE ,SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001).STIntersection(SHAPE).STStartPoint() AS midpoint FROM database.dbo.PATHS WHERE ROW_CHANGED_DATE > @maxDate ) MERGE INTO database.dbo.PATHS_MIDPOINT AS t1 USING PATHS_CTE AS t2 ON t2.UWI = t1.UWI WHEN MATCHED THEN UPDATE SET UWI = t2.UWI ,t1.ROW_CHANGED_DATE = GETDATE() ,t1.SHAPE = t2.midpoint WHEN NOT MATCHED THEN INSERT ( [OBJECTID] ,[UWI] ,ROW_CHANGED_DATE ,[SHAPE] ) VALUES ( rownumber + @oid ,[UWI] ,GETDATE() ,midpoint ) ; Using MERGE statement allows efficient updating and inserting of data into feature classes in one process by filtering on ROW_CHANGED_DATE SQL Server Spatial Queries Examples Example: True Midpoint on Line (User defined function) vs. Pseudo Midpoint on Line (pure ST Functions) (useful for plotting wellbore specific attributes) WORK IN PROGRESS SQL Server Spatial Queries Symbolizing Production Plotted at Surface Hole Location Production Plotted at Wellbore Midpoint SQL Server Non-Spatial Queries SQL Server MGMT Studio – Midpoint on Linestring USE database IF OBJECT_ID('tempdb..#NDIC_HORZ_LIST') IS NOT NULL DROP TABLE #NDIC_HORZ_LIST SET NOCOUNT OFF -- Create temp table to hold the OBJECTID and apply filter on wells without enough points SELECT lineID = identity (int,1,1), OBJECTID INTO #NDIC_HORZ_LIST FROM database.dbo.API12_PATHS CREATE CLUSTERED INDEX idx_NDIC_HORZ_LIST_lineid ON #NDIC_HORZ_LIST(lineID) CREATE INDEX idx_NDIC_HORZ_LIST_objectid ON #NDIC_HORZ_LIST(OBJECTID) DECLARE @midPoint geometry DECLARE @point1X numeric(25,20) DECLARE @point1Y numeric(25,20) DECLARE @point2X numeric(25,20) DECLARE @point2Y numeric(25,20) DECLARE @xDiff numeric(25,20) DECLARE @yDiff numeric(25,20) DECLARE @workingLineLength1 numeric(25,20) = 0 DECLARE @workingLineLength2 numeric(25,20) = 0 DECLARE @lineLength numeric(25,20) DECLARE @i int DECLARE @lineID int DECLARE @lineOID INT Multiple While Loop Operations (analogous to Python function) SLOWER DECLARE @line geometry DECLARE @workLine geometry DECLARE @lastSegment geometry DECLARE @lineFromString NVARCHAR(MAX) SET @lineID = 1 -- Outer loop to iterate over lines in table, using the counts from the temp table WHILE @lineID <= (SELECT COUNT(*) FROM #NDIC_HORZ_LIST) BEGIN -- Assign OBJECTID FROM Temp table to variable SET @lineOID = (SELECT OBJECTID FROM #NDIC_HORZ_LIST WHERE lineID = @lineID) -- Assign SHAPE From table to geometry variable SET @line = (SELECT SHAPE FROM database.dbo.API12_PATHS WHERE OBJECTID = @lineOID) SET @lineLength = @line.STLength() / 2 SET @lineFromString = 'LINESTRING(' SET @i = 1 Loop 1 Loop 2 -- Loop through points in line until the working length is longer than half the line length WHILE @workingLineLength2 < @lineLength BEGIN -- For first point add start point from line IF @i = 1 BEGIN SET @lineFromString = @lineFromString + CAST(@line.STStartPoint().STX AS VARCHAR(10)) + ' ' + CAST(@line.STStartPoint().STY AS VARCHAR(10)) + ')' END ELSE BEGIN -- Strip the trailing ) from the line string text SET @lineFromString = LEFT(@lineFromString, LEN(@lineFromString) - 1) SET @lineFromString = @lineFromString + ',' + CAST(@line.STPointN(@i).STX AS VARCHAR(10)) + ' ' + CAST(@line.STPointN(@i).STY AS VARCHAR(10)) + ')' SET @workLine = geometry::STGeomFromText(@lineFromString, 4267) SET @lastSegment = geometry::STGeomFromText('LINESTRING(' + CAST(@line.STPointN(@i - 1).STX AS VARCHAR(10)) + ' ' + CAST(@line.STPointN(@i - 1).STY AS VARCHAR(10)) + ',' + CAST(@line.STPointN(@i).STX AS VARCHAR(10)) + ' ' + CAST(@line.STPointN(@i).STY AS VARCHAR(10)) + ')', 4267) SET @workingLineLength2 = @workLine.STLength() IF @workingLineLength2 > @lineLength BEGIN SET @point1X = @line.STPointN(@i - 1).STX SET @point1Y = @line.STPointN(@i - 1).STY SET @point2X = @line.STPointN(@i).STX SET @point2Y = @line.STPointN(@i).STY SET @xDiff = (@point2X - @point1X) * ((@lineLength - @workingLineLength1) / @lastSegment.STLength()) SET @yDiff = (@point2Y - @point1Y) * ((@lineLength - @workingLineLength1) / @lastSegment.STLength()) SET @midPoint = geometry::STGeomFromText('POINT(' + CAST(@point1X + @xDiff AS VARCHAR(25)) + ' ' + CAST(@point1Y + @yDiff AS VARCHAR(25)) + ')',4267) INSERT INTO database.dbo.API10_SHL(OBJECTID,SHAPE) SELECT ROW_NUMBER() OVER(ORDER BY t2.OBJECTID), @midPoint FROM database.dbo.API12_PATHS AS t2 END SET @workingLineLength1 = @workingLineLength2 END SET @i = @i + 1 END SET @lineID = @lineID + 1 END SQL Server Non-Spatial Queries SQL Server MGMT Studio – Midpoint on Linestring Add each line segment until half line length + + + + + + SQL Server Spatial Queries SQL Server MGMT Studio – Psuedo Midpoint on Linestring Set Based Operation FASTER SELECT SHAPE.STEnvelope(). STCentroid(). STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001). STIntersection(SHAPE). STStartPoint() FROM WELL_PATHS Find Psuedo Midpoint of Linestring SELECT SHAPE.STEnvelope() Find Psuedo Midpoint of Linestring SELECT SHAPE.STEnvelope(). STCentroid() Find Psuedo Midpoint of Linestring SELECT SHAPE.STEnvelope(). STCentroid(). STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001) Find Psuedo Midpoint of Linestring SELECT SHAPE.STEnvelope(). STCentroid(). STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001). STIntersection(SHAPE) Find Psuedo Midpoint of Linestring SELECT SHAPE.STEnvelope(). STCentroid(). STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001). STIntersection(SHAPE). STStartPoint() Find Psuedo Midpoint of Linestring Statistical Analysis STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001) Hard Coded Number -analyze validity End Point Start Point Distance of Intersection Intersection Buffer Polygon Well Path Find Psuedo Midpoint of Linestring Statistical Analysis ;WITH CTE_PATHS_STATS(dist) AS ( SELECT geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001).STIntersection(SHAPE).STStartPoint().STAsBinary(),4267) .STDistance( geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000001).STIntersection(SHAPE).STEndPoint().STAsBinary(), 4267) ) FROM database.dbo.PATHS ) SELECT MAX(dist) AS max_dist_meter ,MIN(dist) AS min_dist_meter ,AVG(dist) AS avg_dist_meter ,COUNT(dist) AS count_dist ,(SELECT COUNT(*) FROM PATHS_STATS) AS count_total ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 1) AS GreaterThan1m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 5) AS GreaterThan5m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 10) AS GreaterThan10m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 25) AS GreaterThan25m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 50) AS GreaterThan50m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 100) AS GreaterThan100m FROM CTE_PATHS_STATS Find Psuedo Midpoint of Linestring Statistical Analysis ;WITH CTE_PATHS_STATS(dist) AS ( SELECT geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000005).STIntersection(SHAPE).STStartPoint().STAsBinary(),4267) .STDistance( geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000005).STIntersection(SHAPE).STEndPoint().STAsBinary(), 4267) ) FROM database.dbo.PATHS ) SELECT MAX(dist) AS max_dist_meter ,MIN(dist) AS min_dist_meter ,AVG(dist) AS avg_dist_meter ,COUNT(dist) AS count_dist ,(SELECT COUNT(*) FROM PATHS_STATS) AS count_total ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 1) AS GreaterThan1m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 5) AS GreaterThan5m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 10) AS GreaterThan10m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 25) AS GreaterThan25m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 50) AS GreaterThan50m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 100) AS GreaterThan100m FROM CTE_PATHS_STATS Find Psuedo Midpoint of Linestring Statistical Analysis ;WITH CTE_PATHS_STATS(dist) AS ( SELECT geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000004).STIntersection(SHAPE).STStartPoint().STAsBinary(),4267) .STDistance( geography::STGeomFromWKB( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000004).STIntersection(SHAPE).STEndPoint().STAsBinary(), 4267) ) FROM database.dbo.PATHS ) SELECT MAX(dist) AS max_dist_meter ,MIN(dist) AS min_dist_meter ,AVG(dist) AS avg_dist_meter ,COUNT(dist) AS count_dist ,(SELECT COUNT(*) FROM PATHS_STATS) AS count_total ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 1) AS GreaterThan1m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 5) AS GreaterThan5m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 10) AS GreaterThan10m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 25) AS GreaterThan25m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 50) AS GreaterThan50m ,(SELECT COUNT(dist) FROM PATHS_STATS WHERE dist > 100) AS GreaterThan100m FROM CTE_PATHS_STATS Find Psuedo Midpoint of Linestring Process Refinements 1st Pass Midpoint Wellbore with one of the highest Intersection linestring distances Intersection linestring SQL Server Spatial Queries Psuedo Midpoint on Linestring Revised Midpoint Prodedure Intersection Linestring SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE) + .000004).STIntersection(SHAPE). STPointN( Index of Middle Point in Linestring CAST( SHAPE.STEnvelope().STCentroid().STBuffer(SHAPE.STEnvelope().STCentroid().STDistance(SHAPE)+.000004).STIntersection(SHAPE).STNumPoints() / 2 AS INT) ) FROM PATHS Return the at index of Middle Point Find Psuedo Midpoint of Linestring Process Refinements 1st Pass Midpoint 2st Pass Midpoint THE END