1

ポリゴンGIS機能テーブルをESRIパーソナルジオデータベース形式からSQL2008標準形式に移行する作業が必要でした。ソースデータはSQLServerで使用されたばかりです。問題は、形状列をジオメトリに変換することでした。

イタリアのトスカーナ地方で約1,000万のポリゴンに対してこのコードを作成し、正常に使用しました。

/****** 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

ESRIシェープファイルの座標を浮動小数点数に変換するための、インターネット上にある2番目の関数なしでは目標を達成できませんでした。

ポリゴンだけでなく、一般的にすべてのフィーチャタイプで同様の作業を行ったかどうかを尋ねます。また、このコードは同様の問題を解決するのに役立つと思います。

これは私の最初のバージョンです。私はプログラマーからそれを改善する方法やコードの間違いについて学ぶのが好きです。

よろしくお願いします

レオナルド・ダンザ

PS

この関数を使用すると、例のように変換できます。

/****** 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

これは、ESRIPersonalGeodatabaseフィーチャテーブルの形状列をWellKnownTextStringに変換する問題を解決するT-SQL関数の2番目のバージョンです。

このバージョンでは、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 に答える