我的工作需要将多边形 GIS 要素表从 ESRI 个人地理数据库格式迁移到 SQL2008 标准格式。源数据只是在 SQL Server 中使用。问题是将形状列转换为几何。
我为意大利托斯卡纳地区的大约 1000 万个多边形制作并成功使用了这段代码:
/****** object: function [dbo].[shp2wkt] - 12/07/2012 10:02:42 - Leonardo Danza ******/
set ansi_nulls on
go
set quoted_identifier on
go
create function [dbo].[shp2wkt_eng](@shp varbinary(max))
returns nvarchar(max)
as
begin
-- points table
declare @PA table(N int identity(1,1), N_Part_Rel int, X varchar(16), Y varchar(16), part_number int)
declare @N integer, -- row number
@N_Part_Rel integer, -- actual part row number
@X float, @X_STR varchar(16), -- X coord.
@Y float, @Y_STR varchar(16), -- Y coord
@part_number integer -- part sequential number
declare @F bigint -- feature length (byte)
declare @Ftype int -- feature type (5 = polygon)
Declare @Npoints int, @Nparts int -- points number, parts number in the feature
declare @Xmin float, @Ymin float, @XMax float, @YMax float -- bounding box
-- gets feature length
set @F = datalength(@shp)
-- gets Feature Type, bounding box, parts number, points number
set @FType = cast(substring(@shp, 1, 1) as int) -- 1 byte
set @Xmin = dbo.BinToFloatLE(substring(@shp, 5 ,8)) -- 8 byte
set @Ymin = dbo.BinToFloatLE(substring(@shp, 13 ,8)) -- 8 byte
set @XMax = dbo.BinToFloatLE(substring(@shp, 21 ,8)) -- 8 byte
set @YMax = dbo.BinToFloatLE(substring(@shp, 29 ,8)) -- 8 byte
set @NParts = cast(substring(@shp, 37, 1) as int) + -- 4 byte
cast(substring(@shp, 38, 1) as int)*256 +
cast(substring(@shp, 39, 1) as int)*65536 +
cast(substring(@shp, 40, 1) as int)*16777216
set @NPoints = cast(substring(@shp, 41, 1) as int) + -- 4 byte
cast(substring(@shp, 42, 1) as int)*256 +
cast(substring(@shp, 43, 1) as int)*65536 +
cast(substring(@shp, 44, 1) as int)*16777216
-- **********************************************************
-- from byte 44 (45) there is a [Nparts] sequence of
-- 4 byte integers (Nparts is the parts number).
-- Each number is a pointer to the first point of the part.
-- **********************************************************
declare @I int -- points loop counter initialized with @I0
declare @J int -- parts loop counter initialized with 45
declare @I0 int -- current part first point seq. n.
declare @Last_Point int -- current part last point seq. n.
declare @Last_Point_0 int -- prev. part last point seq. n.
declare @Max int -- next part first point seq. n.
declare @Jmax int -- first part first point seq. n.
declare @Part_Id int -- part seq. number (from 0)
set @Part_Id = 0
set @I0 = 44 + 4*@NParts
set @J = 45
set @I = @I0
set @Jmax = 44 + 4*@NParts
set @Last_Point_0 = 0
-- inserts points into @PA table
while @J < @Jmax -- scans all parts
begin
if @Nparts=0 or @Part_Id = @NParts - 1 -- gets current part last point seq. n.
begin
set @Last_Point=@NPoints
end
else
begin
set @Last_Point = cast(substring(@shp, @J+4, 1) as int) +
cast(substring(@shp, @J+5, 1) as int)*256 +
cast(substring(@shp, @J+6, 1) as int)*65536 +
cast(substring(@shp, @J+7, 1) as int)*16777216
end
set @Max = @I0 + 16*(@Last_Point-@Last_Point_0) -- current part points number
While @I < @Max -- scans current part points
begin
Set @N_Part_Rel = 1 + (@I - @I0)/16
Set @X = dbo.BinToFloatLE(substring(@shp, @I + 1, 8))
Set @Y = dbo.BinToFloatLE(substring(@shp,8 + @I + 1, 8))
set @X_STR=cast((cast(@X as decimal(15,8))) as varchar)
set @Y_STR=cast((cast(@Y as decimal(15,8))) as varchar)
insert into @PA (N_Part_Rel, x, y, part_number)
select @N_Part_Rel, @X_STR, @Y_STR, @Part_Id
Set @I = @I + 16
end
set @I0 = 44 + 4*@NParts + 16*@Last_Point
set @J = @J + 4
set @Part_Id = @Part_Id + 1
set @Last_Point_0 = @Last_Point
end
-- create well known text string
declare @wkt nvarchar(max), @part_number_prev int, @virgola varchar(5)
set @wkt='MULTIPOLYGON ((('
set @virgola=''
declare @punti cursor
set @punti = cursor local for select * from @PA
open @punti
fetch next from @punti into @n, @n_part_rel, @x_str,@y_str, @part_number
set @part_number_prev=@part_number
while @@fetch_status = 0
begin
if @part_number_prev<>@part_number -- make separator string
begin
set @virgola='), (' -- virgola is the italian for comma
set @part_number_prev=@part_number
end
else
begin
set @virgola=', '
end
if @n > 1 set @wkt += @virgola -- no separator for first point
set @wkt += @x_str + ' ' + @y_str -- add points coords
fetch next from @punti into @n, @n_part_rel, @x_str,@y_str, @part_number
end
set @wkt+=')))'
close @punti
deallocate @punti
return @wkt
end
/****** Object: UserDefinedFunction [dbo].[BinToFloatLE] Script Date: 07/14/2012 12:19:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE function [dbo].[BinToFloatLE] (
@IEEE64 binary(8)
)
returns float
as
begin
/********************************************************************************/
/* Created By : Umachandar Jayachandran (UC) */
/* Created On : 28 July 2002 */
/* Description : This function can be used to convert a 8-byte IEEE 754 format */
/* representation of real or double-precision value into it's */
/* equivalent floating point value. This cannot be done directly */
/* using CAST in SQL Server. This is an optimized version of the */
/* UDF that does not use lookup tables to check for consistency */
/* or the utility bit function "BitsToFloat". This is used by the*/
/* "ParseTrcShowStats" SP. */
/* Modified : parse binary in little endian order of byte for use in conjuction of ESRI shapefile format
*/
/********************************************************************************/
/* Resources : http://www.umachandar.com/resources.htm */
/********************************************************************************/
-- http://www.umachandar.com/technical/SQL2000Scripts/UtilityFns/Main15.htm
-- IEEE spec:
-- http://www.psc.edu/general/software/packages/ieee/ieee.html
-- little endian
-- http://www.opengroup.org/onlinepubs/9629399/chap14.htm
-- Count from left to right in SQL!
declare @Byte1 binary(1), @Byte2 binary(1), @Byte3 binary(1), @Byte4 binary(1),
@Byte5 binary(1), @Byte6 binary(1), @Byte7 binary(1), @Byte8 binary(1),
@1Float float, @2Float float
set @Byte1 = substring( @IEEE64, 8, 1 )
set @Byte2 = substring( @IEEE64, 7, 1 )
set @Byte3 = substring( @IEEE64, 6, 1 )
set @Byte4 = substring( @IEEE64, 5, 1 )
set @Byte5 = substring( @IEEE64, 4, 1 )
set @Byte6 = substring( @IEEE64, 3, 1 )
set @Byte7 = substring( @IEEE64, 2, 1 )
set @Byte8 = substring( @IEEE64, 1, 1 )
set @1Float = 1.
set @2Float = 2.
-- Double-precision float per IEEE 754 specification.
-- S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-- 0 1 11 12 63
-- Sign * 2^Exp * Val
return case @IEEE64 when 0x0 then 0 else 1 end *
case sign( cast( @Byte1 as smallint ) & 0x02 ) when 0 then 1 else -1 end *
( @1Float +
( sign( @Byte8 & power( 2, 0 ) ) * power( @2Float, -52 ) ) +
( sign( @Byte8 & power( 2, 1 ) ) * power( @2Float, -51 ) ) +
( sign( @Byte8 & power( 2, 2 ) ) * power( @2Float, -50 ) ) +
( sign( @Byte8 & power( 2, 3 ) ) * power( @2Float, -49 ) ) +
( sign( @Byte8 & power( 2, 4 ) ) * power( @2Float, -48 ) ) +
( sign( @Byte8 & power( 2, 5 ) ) * power( @2Float, -47 ) ) +
( sign( @Byte8 & power( 2, 6 ) ) * power( @2Float, -46 ) ) +
( sign( @Byte8 & power( 2, 7 ) ) * power( @2Float, -45 ) ) +
( sign( @Byte7 & power( 2, 0 ) ) * power( @2Float, -44 ) ) +
( sign( @Byte7 & power( 2, 1 ) ) * power( @2Float, -43 ) ) +
( sign( @Byte7 & power( 2, 2 ) ) * power( @2Float, -42 ) ) +
( sign( @Byte7 & power( 2, 3 ) ) * power( @2Float, -41 ) ) +
( sign( @Byte7 & power( 2, 4 ) ) * power( @2Float, -40 ) ) +
( sign( @Byte7 & power( 2, 5 ) ) * power( @2Float, -39 ) ) +
( sign( @Byte7 & power( 2, 6 ) ) * power( @2Float, -38 ) ) +
( sign( @Byte7 & power( 2, 7 ) ) * power( @2Float, -37 ) ) +
( sign( @Byte6 & power( 2, 0 ) ) * power( @2Float, -36 ) ) +
( sign( @Byte6 & power( 2, 1 ) ) * power( @2Float, -35 ) ) +
( sign( @Byte6 & power( 2, 2 ) ) * power( @2Float, -34 ) ) +
( sign( @Byte6 & power( 2, 3 ) ) * power( @2Float, -33 ) ) +
( sign( @Byte6 & power( 2, 4 ) ) * power( @2Float, -32 ) ) +
( sign( @Byte6 & power( 2, 5 ) ) * power( @2Float, -31 ) ) +
( sign( @Byte6 & power( 2, 6 ) ) * power( @2Float, -30 ) ) +
( sign( @Byte6 & power( 2, 7 ) ) * power( @2Float, -29 ) ) +
( sign( @Byte5 & power( 2, 0 ) ) * power( @2Float, -28 ) ) +
( sign( @Byte5 & power( 2, 1 ) ) * power( @2Float, -27 ) ) +
( sign( @Byte5 & power( 2, 2 ) ) * power( @2Float, -26 ) ) +
( sign( @Byte5 & power( 2, 3 ) ) * power( @2Float, -25 ) ) +
( sign( @Byte5 & power( 2, 4 ) ) * power( @2Float, -24 ) ) +
( sign( @Byte5 & power( 2, 5 ) ) * power( @2Float, -23 ) ) +
( sign( @Byte5 & power( 2, 6 ) ) * power( @2Float, -22 ) ) +
( sign( @Byte5 & power( 2, 7 ) ) * power( @2Float, -21 ) ) +
( sign( @Byte4 & power( 2, 0 ) ) * power( @2Float, -20 ) ) +
( sign( @Byte4 & power( 2, 1 ) ) * power( @2Float, -19 ) ) +
( sign( @Byte4 & power( 2, 2 ) ) * power( @2Float, -18 ) ) +
( sign( @Byte4 & power( 2, 3 ) ) * power( @2Float, -17 ) ) +
( sign( @Byte4 & power( 2, 4 ) ) * power( @2Float, -16 ) ) +
( sign( @Byte4 & power( 2, 5 ) ) * power( @2Float, -15 ) ) +
( sign( @Byte4 & power( 2, 6 ) ) * power( @2Float, -14 ) ) +
( sign( @Byte4 & power( 2, 7 ) ) * power( @2Float, -13 ) ) +
( sign( @Byte3 & power( 2, 0 ) ) * power( @2Float, -12 ) ) +
( sign( @Byte3 & power( 2, 1 ) ) * power( @2Float, -11 ) ) +
( sign( @Byte3 & power( 2, 2 ) ) * power( @2Float, -10 ) ) +
( sign( @Byte3 & power( 2, 3 ) ) * power( @2Float, -09 ) ) +
( sign( @Byte3 & power( 2, 4 ) ) * power( @2Float, -08 ) ) +
( sign( @Byte3 & power( 2, 5 ) ) * power( @2Float, -07 ) ) +
( sign( @Byte3 & power( 2, 6 ) ) * power( @2Float, -06 ) ) +
( sign( @Byte3 & power( 2, 7 ) ) * power( @2Float, -05 ) ) +
( sign( @Byte2 & power( 2, 0 ) ) * power( @2Float, -04 ) ) +
( sign( @Byte2 & power( 2, 1 ) ) * power( @2Float, -03 ) ) +
( sign( @Byte2 & power( 2, 2 ) ) * power( @2Float, -02 ) ) +
( sign( @Byte2 & power( 2, 3 ) ) * power( @2Float, -01 ) ) ) *
power( @2Float,
( sign( @Byte2 & power( 2, 4 ) ) * power( 2, 00 ) ) +
( sign( @Byte2 & power( 2, 5 ) ) * power( 2, 01 ) ) +
( sign( @Byte2 & power( 2, 6 ) ) * power( 2, 02 ) ) +
( sign( @Byte2 & power( 2, 7 ) ) * power( 2, 03 ) ) +
( sign( @Byte1 & power( 2, 0 ) ) * power( 2, 04 ) ) +
( sign( @Byte1 & power( 2, 1 ) ) * power( 2, 05 ) ) +
( sign( @Byte1 & power( 2, 2 ) ) * power( 2, 06 ) ) +
( sign( @Byte1 & power( 2, 3 ) ) * power( 2, 07 ) ) +
( sign( @Byte1 & power( 2, 4 ) ) * power( 2, 08 ) ) +
( sign( @Byte1 & power( 2, 5 ) ) * power( 2, 09 ) ) +
( sign( @Byte1 & power( 2, 6 ) ) * power( 2, 10 ) ) - 1023 )
--- verificare il bit di segno.....
end
如果没有在 Internet 上找到的用于将 ESRI shapefile 坐标转换为浮点数的第二个函数,我就没有达到目标。
我问是否有人不仅为多边形做了类似的工作,而且对所有特征类型都做了类似的工作。我也认为这段代码可能对解决类似问题很有用。
这是我的第一个版本。我喜欢向程序员学习如何改进它或了解代码中的错误。
此致
莱昂纳多·丹萨
附言
使用此功能,您可以如示例中所示进行转换:
/****** Object: Table [dbo].[si_test_part] Script Date: 07/14/2012 13:06:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[test_shp2geom](
[OBJECTID] [int] NOT NULL,
[SHAPE] [image] NULL,
[geom] [geometry] NULL,
CONSTRAINT [FDO_OBJECTID_test_shp2geom] PRIMARY KEY CLUSTERED
(
[OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
insert test_shp2geom
select
1 as objectid,
0x05000000F6285C8FD12E3A418FC2F558472352415C8FC2751A2F3A4148E17A3461235241010000000B00000000000000F6285C8FD12E3A4148E17A3461235241A4703DCADD2E3A41AE47E1BA5F2352413E0AD7630A2F3A41295C8FF25A235241EC51B81E0D2F3A413E0AD7834E2352415C8FC2751A2F3A411F85EB914B2352411F85EB91162F3A418FC2F55847235241295C8FC2F12E3A418FC2F5384A23524185EB51F8E02E3A41CDCCCC4C4A23524148E17A94D92E3A41713D0A0755235241E17A142EDA2E3A41E17A141E55235241F6285C8FD12E3A4148E17A3461235241
as shape, null as geom
union all
select
3,
0x0500000052B81E054F2E3A4185EB51F84723524185EB51F8E02E3A419A9999B960235241010000000D0000000000000085EB51F8E02E3A41CDCCCC4C4A23524190C2F528D52E3A41B81E855B4A2352411F85EB91722E3A4185EB51F84723524152B81E054F2E3A4115AE470155235241CDCCCC0CA62E3A416766667660235241AE47E17AB52E3A419A9999B96023524115AE47A1A62E3A41EC51B81E5C235241F1AFE755B02E3A41A520987A582352413E0AD7639E2E3A413E0AD793552352411F85EB11A72E3A41CDCCCC5C50235241D7A370FDC02E3A41D7A3706D5123524148E17A94D92E3A41713D0A075523524185EB51F8E02E3A41CDCCCC4C4A235241
as shape, null as geom
union all
select
4,
0x05000000E17A14AEEC323A41B81E852B4423524185EB51F878333A41A4703D2A5E235241010000000B00000000000000E17A14AEEC323A41AE47E1DA5423524185EB51F878333A41A4703D2A5E235241676666A66D333A4185EB51F852235241000000C064333A41C3F5284C4C235241C3F528DC49333A413E0AD76349235241676666262F333A41F6285C1F49235241C3F5281C07333A41B81E852B44235241EC51B8DE04333A4185EB51684423524185EB513803333A41E17A14FE4623524185EB51F8F9323A41676666F64A235241E17A14AEEC323A41AE47E1DA54235241
as shape, null as geom
union all
select
5,
0x0500000085EB51F8E02E3A41A4703D5A2E2352410AD7A3F04B2F3A41295C8FF25A23524101000000130000000000000085EB51F8E02E3A41CDCCCC4C4A235241295C8FC2F12E3A418FC2F5384A2352411F85EB91162F3A418FC2F558472352415C8FC2751A2F3A411F85EB914B235241EC51B81E0D2F3A413E0AD7834E2352413E0AD7630A2F3A41295C8FF25A235241E17A14AE332F3A4148E17A845623524185EB5138442F3A4115AE47214B2352410AD7A330412F3A4115AE47C14A2352415C8FC2B5352F3A41D7A3703D47235241CDCCCC8C442F3A41CDCCCC7C3A2352410AD7A3F04B2F3A41295C8F2234235241CDCCCC4C412F3A410AD7A3602E235241A4703D8A3D2F3A41A4703D5A2E2352415C8FC2F5352F3A41C3F528EC31235241B81E852B032F3A417B14AE573523524152B81E45F02E3A41AE47E12A3623524185EB51B8EC2E3A41295C8F923923524185EB51F8E02E3A41CDCCCC4C4A235241
as shape, null as geom
union all
select
6,
0x05000000E17A14AE332F3A4115AE47214B23524115AE4761A32F3A4148E17A8456235241010000000A00000000000000E17A14AE332F3A4148E17A845623524133333373462F3A4115AE478154235241D7A3707D9D2F3A410AD7A3D05323524115AE4761A32F3A41A4703D8A4F23524115AE4721822F3A416766660651235241E17A14EE6B2F3A419A9999395123524148E17A145B2F3A413E0AD7034D2352419A9999194E2F3A41AE47E15A4C23524185EB5138442F3A4115AE47214B235241E17A14AE332F3A4148E17A8456235241
as shape, null as geom
union all
select
7,
0x05000000333333F3112E3A4115AE47012E235241676666A69C2E3A4115AE470155235241010000001400000000000000E17A14EE122E3A4115AE47414C23524152B81E054F2E3A4115AE4701552352411F85EB91722E3A4185EB51F8472352411F85EB51642E3A41000000A047235241CDCCCCCC652E3A41EC51B83E3E235241C3F5281C802E3A41333333833E235241000000C0882E3A415C8FC2B533235241676666A69C2E3A41C3F5282C32235241713D0A57942E3A4115AE47012E235241000000C06D2E3A4148E17A642F235241EC51B89E5E2E3A41713D0AC7322352411F85EB91632E3A4115AE473137235241EC51B8DE5E2E3A41D7A3709D3B235241E17A146E4E2E3A41CDCCCCFC3A2352410AD7A3702B2E3A419A99991940235241713D0A171D2E3A417B14AE573D23524100000080152E3A413E0AD7233E235241333333F3112E3A415C8FC27541235241000000401D2E3A4115AE472146235241E17A14EE122E3A4115AE47414C235241
as shape, null as geom
union all
select
8,
0x05000000B81E856BE1323A4185EB516844235241EC51B8DE04333A41AE47E1DA54235241010000000800000000000000B81E856BE1323A4115AE47615223524100000000E3323A416766663654235241E17A14AEEC323A41AE47E1DA5423524185EB51F8F9323A41676666F64A23524185EB513803333A41E17A14FE46235241EC51B8DE04333A4185EB5168442352413E0AD763F7323A4148E17A3445235241B81E856BE1323A4115AE476152235241
as shape, null as geom
union all
select
9,
0x05000000A4703D4AA1323A41AE47E1CA4623524148E17A54E3323A41295C8FF253235241010000000A00000000000000A4703D4AA1323A415C8FC2F54F235241295C8F42DD323A41295C8FF2532352417B14AE87DC323A41F6285C1F532352411F85EB51DA323A41CDCCCCEC51235241E17A142EDA323A4148E17AE44F235241D7A370BDDE323A41CDCCCCFC4A23524148E17A54E3323A41AE47E1CA4623524148E17A14C4323A41E17A144E4A2352410AD7A3B0B1323A41F6285C5F4C235241A4703D4AA1323A415C8FC2F54F235241
as shape, null as geom
union all
select
10,
0x05000000D7A3707D9D2F3A41B81E853B49235241000000402E303A410AD7A3D053235241010000000A00000000000000D7A3707D9D2F3A410AD7A3D0532352410AD7A3702C303A418FC2F5B852235241000000402E303A4148E17AC450235241EC51B8DEFE2F3A41A4703DFA4D235241A4703D0AD22F3A417B14AE974B23524100000040AB2F3A41B81E853B4923524152B81E05AB2F3A41EC51B8FE49235241333333B3A92F3A41333333F34A23524115AE4761A32F3A41A4703D8A4F235241D7A3707D9D2F3A410AD7A3D053235241
as shape, null as geom
union all
select
11,
0x050000000AD7A3702C303A41E17A143E4C235241B81E85EB2A313A418FC2F5B852235241010000000C000000000000000AD7A3702C303A418FC2F5B8522352411F85EB11F7303A415C8FC23551235241B81E852BFE303A413E0AD7D34F23524115AE47A109313A417B14AEA74E2352417B14AE4718313A41000000104E235241B81E85EB2A313A41E17A143E4C2352416766666626313A41000000804C235241F6285C0FFA303A4115AE47114E2352417B14AEC78A303A41333333F35023524185EB51F845303A41A4703D2A52235241000000402E303A4148E17AC4502352410AD7A3702C303A418FC2F5B852235241
as shape, null as geom
go
update test_shp2geom
set geom=geometry::STGeomFromText(dbo.shp2wkt(SHAPE),0).MakeValid()
select geom from test_shp2geom