1

我的工作需要将多边形 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
4

1 回答 1

1

这是 T-SQL 函数的第二个版本,解决了将 ESRI 个人地理数据库要素表的形状列转换为众所周知的文本字符串的问题。

在这个版本中,我添加了对 XY WKT POINT、LINESTRING、MULTILINESTRING、POLYGON、MULTIPOLYGON、MULTIPOINT 的支持。这个版本检查多边形/多多边形环的有符号区域,计算外环和内环,并使用这些信息来管理 WKT 字符串的类型和语法。

可能你们中没有人对此类问题感兴趣/参与其中......无论如何,谢谢,并致以最诚挚的问候

莱昂纳多·丹萨

/****** object:  function [dbo].[shp2wkt_eng] - 12/07/2012 10:02:42 - Leonardo Danza ******/
set ansi_nulls on
go
set quoted_identifier on
go
alter function [dbo].[shp2wkt_eng](@shp  varbinary(max))
returns nvarchar(max) 
as
begin
    -- points table

    declare @Points_A 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

    -- parts table

    declare @Parts_A table(part_number integer, SignedArea float)

    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
    declare @OuterRings int, @InnerRings int                   -- current number of outer and inner rings in case of polygons
    declare @SignedArea float, @X_Prev float, @Y_Prev float    -- current ring area and previous point coords

    set @OuterRings = 0; set @InnerRings =0; set @SignedArea= 0;

    -- 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 @Points_A 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
            set @SignedArea=0

            While @I < @Max  -- scans current part points
                begin
                    Set @N_Part_Rel = 1 + (@I - @I0)/16 
                    set @X_Prev=@X; set @Y_Prev=@Y;
                    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 @Points_A (N_Part_Rel, x, y, part_number)
                    select @N_Part_Rel, @X_STR, @Y_STR, @Part_Id

                    -- update current Signed Area

                    if @I>@I0 set @SignedArea+=(@X-@X_Prev) *( @Y+@Y_Prev)
                    Set @I = @I + 16
                end

            -- insert row into Parts Table

            insert @Parts_A(part_number, SignedArea) select @Part_Id, @SignedArea/2

            if @Ftype=5 and @SignedArea<>0
                begin
                    if @SignedArea>0 set @OuterRings+=1 else set @InnerRings+=1
                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), @end_wkt varchar(10), @part_number_prev int, @virgola varchar(50), @SignedArea_next float

    set @wkt=         -- set strings start wkt,end wkt
        case @Ftype 
             when 1 then 'POINT(|)' 
             when 3 then 
                 case
                     when @OuterRings>1 then 'MULTILINESTRING((|))'
                     when @OuterRings<=1 then 'LINESTRING(|)'
                 end
             when 5 then 
                 case
                     when @OuterRings>1 then 'MULTIPOLYGON(((|)))'
                     when @OuterRings<=1 then 'POLYGON((|))'
                 end
             when 8 then 'MULTIPOINT(|)'
        end 
        set @end_wkt=right(@wkt,len(@wkt) - charindex('|',@wkt))
        set @wkt=left(@wkt,charindex('|',@wkt)-1)

    set @virgola=''

    declare @punti cursor
    set @punti = cursor local for select Po.*, Pa.SignedArea from @Points_A Po inner join @Parts_A Pa on Po.part_number=Pa.part_number order by Po.n
    open @punti 
    fetch next from @punti into @n, @n_part_rel, @x_str,@y_str, @part_number, @SignedArea
    select @SignedArea_next=0
    set @part_number_prev=@part_number
    while @@fetch_status = 0
        begin
            if @part_number_prev<>@part_number  -- make separator string 
                begin
                    if @SignedArea>0      
                        begin                           -- new outer ring
                            set @virgola=')), (('       -- virgola is the italian for comma
                        end
                    else
                        begin                           -- new inner ring
                            set @virgola='), ('         -- virgola is the italian for comma
                        end
                    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, @SignedArea
            select @SignedArea_next=isnull(SignedArea,0) from @Parts_A where part_number=(@part_number+1)
        end
    set @wkt+=@end_wkt
    close @punti
    deallocate @punti
    return @wkt
end
于 2012-07-17T13:11:05.443 回答