MSSQLで電子マップタむルに描画する

habrコミュニティの読者に、CLRラむブラリMicrosoft.SqlServer.Typesを䜿甚しお、電子地図のタむルを䜜成する方法を䌝えたいず思いたす。 この蚘事では、さらにレンダリングするためのマップタむルのリストの生成に぀いお説明したす。 MS SQL 2008デヌタベヌスに栌玍されおいるオブゞェクトのゞオメトリによっおタむルを生成するアルゎリズムに぀いお説明し、党䜓のレンダリングプロセスに぀いおは、蚘事の最埌の䟋で段階的に説明したす。



内容




問題
゜ヌスデヌタ
解決策
タむルリポゞトリ
タむルの準備手順
䜿甚する機胜
ポリラむンの䟋
亀差点チェック
タむル画像を保存するためのテヌブル
タむルにアむコンを配眮する
タむルの関連付け
タむル䞊のゞオメトリの描画
おわりに


問題




ブラりザヌが倧量の地理デヌタをベクタヌグラフィックスで衚瀺する堎合SVGたたはCANVASを䜿甚、デヌタがクラむアントマシンにダりンロヌドされるのを埅぀だけでなく、描画プロセスに時間がかかりすぎるこずもありたす。
ブラりザヌの地図に倚数のアむコンを衚瀺する堎合、クラスタリングを適甚できたすが、耇雑な幟䜕孊的オブゞェクトの堎合は別のアプロヌチを䜿甚する必芁がありたす。


゜ヌスデヌタ




ゞオメトリオブゞェクトのセットはMicrosoft SQL 2008デヌタベヌステヌブルに栌玍され、ノヌドの座暙は緯床ず経床EPSG4326です。 地理デヌタフィヌルドのタむプはGEOMETRYです。 オブゞェクトは、 ポむントゞオメトリのアむコンずしおマップ䞊に衚瀺される必芁がありたす。 ポリラむンゞオメトリの特定の倪さのポリラむンの圢匏。 ポリゎンゞオメトリは、アりトラむン付きの1぀以䞊のシェヌディングポリゎンずしお衚瀺する必芁がありたす。 タむルはWebメルカトル図法ず䞀臎する必芁がありたす

解決策




ベクタヌグラフィックスの代わりに、マップ䞊のオブゞェクトをラスタヌレむダヌずしお、぀たりマップ自䜓ず同じ画像タむルで衚瀺したす。 これを行うには、オブゞェクトの画像を䜿甚しお各スケヌルのマップタむルのセットを準備する必芁がありたす。 タむルを䜜成するには、 Google Webメルカトル図法を䜿甚したす 。぀たり、 メルカトル図法を説明する匏が䜿甚される Googleコヌドを䜿甚しお、緯床ず経床のマップピクセルぞの倉換が実行されたす。
予枬の詳现に぀いおは、 こちらをご芧ください 。
Sql Server 2008のバヌゞョン以降、 GEOMETRYおよびGEOGRAPHYデヌタ型は 、空間デヌタを操䜜するためにサポヌトされおいたす。
Yandex、Google、たたはOpenStreetMapマッピングの既存のマッピングサヌビス。PNG画像の圢匏で提䟛され、通垞は256x256ピクセルの固定サむズです。 珟圚、SVGやCANVASなどのテクノロゞヌを䜿甚しおタむルが圢成されるサヌビスがありたす。 PNG圢匏画像ネットワヌクグラフィックのラスタヌタむルを怜蚎したす。 PNG圢匏は透明床アルファチャンネルで瀺されるをサポヌトしたす。これにより、耇数のレむダヌを衚瀺するずきに、絶察にオヌバヌラップするこずなくタむルを重ねるこずができたす。

タむルリポゞトリ




各スケヌルに察しお、特定のタむルセットが保存されたす。 0レベル-1タむルのスケヌルの堎合
画像

第1レベルスケヌルの堎合、4぀の2 * 2タむル

スケヌルnの堎合、2n * 2nタむルが保存されたす。 スケヌル数が増加するタむルの数は指数関数的に増加したす。

タむルは、Webサヌバヌのファむルシステムに保存され、http芁求によっおクラむアントマシンに送信されたす。次に䟋を瀺したす。
someurl / layer {Z} / {X} / {Y} .png
ここで、Z、X、Yはそれぞれスケヌリング、Xタむル䜍眮、Yタむル䜍眮です。 たずえば、次のURLで、サンクトペテルブルクのトリニティブリッゞの画像を含むタむルを䜿甚できたす。
b.tile.openstreetmap.org/15/19144/9524.png
このURLで
15-スケヌル番号。
19144– Xタむルの䜍眮。
9524-Yタむルの䜍眮。
圓然、異なるシステムではリク゚ストURLの圢匏は異なりたす。 タむル番号ずスケヌルの代わりに、 QUADキヌを䜿甚しおタむルをリク゚ストできたす。 タむルファむルは、サヌバヌによっおファむルシステムから盎接、たたはhttpハンドラヌを䜿甚しおクラむアントに送信できたす。 X、Y、Zのオプションを怜蚎したす。

タむルの準備手順




  • 各オブゞェクトのゞオメトリに応じたタむルのリストの圢成。
  • 各オブゞェクトのタむルの生成。
  • 䞀意のセットを取埗するためのタむルの関連付け。
  • ファむルシステムに保存しおいたす。


䜿甚する機胜




タスクを実装するには、タむルのX、Y䜍眮およびスケヌル番号によっおタむルゞオメトリを圢成する機胜が必芁になりたす。 この堎合のタむルゞオメトリは、緯床ず経床で衚された角床の座暙でタむルを芆う長方圢です。 このようなゞオメトリの圢成は、SQL関数たたはSQL CLR関数で実装できたす。 SQL CLR関数ず通垞のSQL関数の実行時間の違いは目立ちたせん。 SQL CLR関数コヌドは、添付゜ヌスコヌドのCoords2PixelConversionクラスに実装されおいたす。
次のゞオメトリは、このタむルの茪郭です。぀たり、その境界に沿っおいたす。 ここのピヌクの座暙は経床ず緯床です。
タむルボヌダヌゞオメトリ
'POLYGON ((30.322265625 59.955010262062061, 30.322265625 59.949509172252277, 30.333251953125 59.949509172252277, 30.333251953125 59.955010262062061, 30.322265625 59.955010262062061))' 


tile.GetTileBoundsのスカラヌSQLコヌド
 tile.GetTileBounds(@level int, @x int, @y int) CREATE FUNCTION [tile].[GetTileBounds] (@level int, @x int, @y int) RETURNS geometry AS BEGIN DECLARE @res GEOMETRY = NULL IF @level IS NOT NULL AND @x IS NOT NULL AND @y IS NOT NULL BEGIN DECLARE @n1 FLOAT = PI() - 2.0 * PI() * @y / POWER(2.0, @level); DECLARE @n2 FLOAT = PI() - 2.0 * PI() * (@y + 1) / POWER(2.0, @level); DECLARE @top FLOAT = (180.0 / PI() * ATAN(0.5 * (EXP(@n1) - EXP(-@n1)))); DECLARE @bottom FLOAT = (180.0 / PI() * ATAN(0.5 * (EXP(@n2) - EXP(-@n2)))); DECLARE @tileWidth FLOAT = 360 / CONVERT(float, POWER(2, @level)) DECLARE @left FLOAT = @tileWidth * @x - 180, @right FLOAT = @tileWidth * (@x + 1) - 180 SET @res = geometry::STPolyFromText('POLYGON ((' + LTRIM(STR(@left, 25, 16)) + ' ' + LTRIM(STR(@top, 25, 16)) + ', ' + LTRIM(STR(@left, 25, 16)) + ' ' + LTRIM(STR(@bottom, 25, 16)) + ', ' + LTRIM(STR(@right, 25, 16)) + ' ' + LTRIM(STR(@bottom, 25, 16)) + ', ' + LTRIM(STR(@right, 25, 16)) + ' ' + LTRIM(STR(@top, 25, 16)) + ', ' + LTRIM(STR(@left, 25, 16)) + ' ' + LTRIM(STR(@top, 25, 16)) + '))', 0) END RETURN @res END 


この機胜の䜿甚方法に぀いおは、この蚘事の埌半で説明したす。

タむルのリストを䜜成する方法を怜蚎しおください。 さたざたなゞオメトリに察しお、タむルのリストを圢成するためのさたざたなアプロヌチを遞択できたす。

方法1

この状況を䜿甚したす。1぀のスケヌルでオブゞェクトがタむルず亀差しない堎合、倧きい数倀4のスケヌルでは、チェック察象のタむルず重なるタむルもオブゞェクトず亀差したせん。 ぀たり、次のスケヌルに移動するずき、前のスケヌルのタむルがオブゞェクトのゞオメトリず亀差する堎合にのみ、タむルをチェックしたす。 これにより、方法2で実行される䞍芁なチェックがなくなりたす。

方法2

実際、この方法は最悪のシナリオです。 各オブゞェクトの各瞮尺に぀いお、 GEOMETRY :: STEnvelope関数を䜿甚しおタむルのサブセットを決定し、このサブセットのタむルずオブゞェクトの亀差を確認したす。 この方法は、より倚くのタむルがチェックされるため、特に面積たたは長さが倧きいオブゞェクトの堎合は効果が䜎くなりたす。

方法3

各オブゞェクトに぀いお、タむルメッシュのゞオメトリを圢成し、メッシュずオブゞェクトのゞオメトリの亀差郚で、ポむントのセットを取埗したす。 取埗した各ポむントに぀いお、2぀のタむルを定矩し、タむルの最終リストに远加したす。たずえば、倧陞を通過する耇雑な地理的ラむンの堎合、タむルの境界に沿っお通過するグリッドずの亀点を芋぀け、これらのポむントからレンダリングするタむルを既に決定できたす。 グリッドは、線を含む長方圢領域の境界内に䜜成され、垂盎線ず氎平線のセットです。 これは、オブゞェクトの長方圢領域の境界内にあるすべおのタむルをチェックするよりもはるかに効果的です。
最初の方法に぀いお詳しく説明したす。 領域を持぀オブゞェクトのゞオメトリの堎合、オブゞェクトずの亀差をチェックするためのタむルのセットは、オブゞェクトず重なる長方圢の領域bboxの極端なタむルに制限できたす。
オブゞェクトのゞオメトリ POINTゞオメトリタむプを陀くに埓っお、CLRの四角圢はMSSQL関数GEOMETRY :: STEnvelopeによっお圢成されたす。 POINTゞオメトリを持぀オブゞェクトの堎合、長方圢の領域がbboxの境界ずしお䜿甚され、地図䞊のオブゞェクトのアむコンず重なりたす。 重耇するアむコンのゞオメトリを返すGetImageBound関数は、 GoogleProjectionクラスに実装されおいたす。 たた、緯床ず経床をピクセル䜍眮番号に倉換するメ゜ッドも実装しおいたす。 長方圢領域のコヌナヌポむントの座暙は、緯床ず経床で衚されたす。 次に、結果の長方圢をカバヌするタむルのサブセットを取埗したす。 これを行うには、地理座暙を適切なスケヌルのタむル番号に倉換する機胜が必芁です。 経床ず緯床でタむルのX䜍眮ずY䜍眮を取埗するには、埌で説明するSQL CLR関数ず、以䞋に瀺すSQL関数の䞡方を䜿甚できたす。

 tile.GetXTilePos((@Longitude FLOAT, @Zoom INT) tile.GetYTilePos((@Latitude FLOAT, @Zoom INT) 


コヌナヌタむルの䜍眮を決定した埌、怜出されたコヌナヌタむル間の長方圢領域にあるすべおのタむルは、 tile.fn_FetchGeometryTilesZoomDepth関数でオブゞェクトのゞオメトリずの亀差に぀いおチェックされたす。

経床ずスケヌル番号のXタむル䜍眮を取埗するSQL関数
 CREATE FUNCTION tile.GetXTilePos(@Longitude FLOAT, @Zoom INT) RETURNS INT AS BEGIN DECLARE @D FLOAT,@E FLOAT,@F FLOAT,@G FLOAT, @tileY INT, @tileX INT SET @D = 128 * POWER(2, @Zoom) SET @E = ROUND(@D + @Longitude * 256 / 360 * POWER(2, @Zoom), 0) SET @tileX = Floor(@E / 256); RETURN @tileX END 



緯床ず瞮尺番号のタむルのY䜍眮を取埗する機胜
 CREATE FUNCTION tile.GetYTilePos(@Latitude FLOAT, @Zoom INT) RETURNS INT AS BEGIN DECLARE @A FLOAT, @B FLOAT, @C FLOAT, @D FLOAT, @E FLOAT, @F FLOAT, @G FLOAT, @tileY INT SET @D = 128 * POWER(2, @Zoom) SET @A = Sin(PI() / 180 * @Latitude) SET @B = -0.9999 SET @C = 0.9999 IF @A < @B SET @A = @B IF @A > @C SET @A = @C SET @F = @A SET @G = Round(@D + 0.5 * Log((1.0 + @F) / (1.0 - @F)) * (-256) * POWER(2, @Zoom) / (2 * PI()),0) SET @tileY = Floor(@G / 256) RETURN @tileY END 


tile.fn_FetchGeometryTilesZoomDepth関数は、ゞオメトリをカバヌする最小の長方圢領域の巊䞊ず右䞋のタむルを蚈算したす。 次に、ネストされたルヌプ内の図ずタむルの亀差点を決定するために、この領域の各タむルに察しおtile.fn_GetTilesByTileNumZoomDepth関数を䜿甚したす。巊から右、䞊から䞋、巊䞊から右䞋に移動したす。 この関数は、オブゞェクトのゞオメトリずの亀差が定矩されおいるタむルのリストを返したす。

タむルセット機胜
 CREATE FUNCTION tile.fn_FetchGeometryTilesZoomDepth ( @GeoData GEOMETRY, @Zoom INT, @Depth INT) RETURNS @retTiles TABLE ( Zoom INT, TileX INT, TileY INT) AS BEGIN DECLARE @Left FLOAT, @Right FLOAT, @Top FLOAT, @Bottom FLOAT DECLARE @CurrentXTile INT, @CurrentYTile INT, @Quanttiles INT DECLARE @Envelope GEOMETRY, @RightTop GEOMETRY, @LeftBottom GEOMETRY DECLARE @CurTileGeom GEOMETRY, @res GEOMETRY DECLARE @tiletop FLOAT,@tilebottom FLOAT,@tileleft FLOAT, @tileright FLOAT DECLARE @LeftTilePos INT,@RightTilePos INT,@TopTilePos INT DECLARE @BottomTilePos INT SET @envelope = @GeoData.STEnvelope() SET @RightTop = @envelope.STPointN(3) SET @LeftBottom = @envelope.STPointN(1) SET @Right = @RightTop.STX SET @Left = @LeftBottom.STX SET @Top = @RightTop.STY SET @Bottom = @LeftBottom.STY SET @LeftTilePos = tile.GetXTilePos( @Left,@Zoom) SET @RightTilePos = tile.GetXTilePos( @Right,@Zoom) SET @TopTilePos = tile.GetYTilePos( @Top,@Zoom) SET @BottomTilePos = tile.GetYTilePos( @Bottom,@Zoom) SET @CurrentXTile = @LeftTilePos WHILE @CurrentXTile <= @RightTilePos BEGIN SET @currentYTile = @TopTilePos WHILE @CurrentYTile <= @BottomTilePos BEGIN INSERT INTO @retTiles (Zoom, TileX, TileY) SELECT * FROM tile.fn_GetTilesByTileNumZoomDepth ( @GeoData, @Zoom, @CurrentXTile, @CurrentYTile, @Depth ) SET @CurrentYTile = @CurrentYTile + 1 END SET @CurrentXTile =@CurrentXTile + 1 END RETURN END 


タむルゞオメトリずオブゞェクトのゞオメトリの亀差を確認するには、GEOMETRY :: STIntersects関数を䜿甚したす。 オブゞェクトのゞオメトリずタむルのゞオメトリが亀差する堎合、以前に䜜成したtile.TileOverlapテヌブルに゚ントリを远加し、次のスケヌルの4぀のタむルに察しお同じ関数を再垰的に呌び出したす。パラメヌタ@Depthを1枛らしお、珟圚のスケヌルをカバヌしたす。 亀差チェックは、関数tile.fn_FetchGeometryTilesZoomDepthで実装されたす。

指定されたタむルのゞオメトリによるタむルのリストの取埗
 CREATE FUNCTION tile.fn_GetTilesByTileNumZoomDepth ( @GeoData GEOMETRY, @Zoom INT, @tileX INT, @tileY INT, @Depth INT) RETURNS @retTiles TABLE ( Zoom INT, X INT, Y INT) AS BEGIN DECLARE @currentTile TABLE ( Zoom INT, X INT, Y INT) IF GEOGRAPHY::STGeomFromWKB([tile].[GetTileBounds](@Zoom, @tileX, @tileY).STAsBinary(),4326).STIntersects(GEOGRAPHY::STGeomFromWKB(@GeoData.MakeValid().STUnion(@GeoData.STStartPoint()).STAsBinary(),4326)) = 1 BEGIN INSERT INTO @currentTile SELECT @Zoom , @tileX , @tileY INSERT INTO @retTiles SELECT d.zoom, dX, dY FROM @currentTile c CROSS APPLY (SELECT * FROM [tile].[fn_GetTilesForObjectByTileNumZoomDepth]( @GeoData , c.Zoom + 1, cX * 2, cY * 2, @Depth - 1) WHERE @Depth > 0) AS d INSERT INTO @retTiles SELECT d.zoom, dX, dY FROM @currentTile c CROSS APPLY (SELECT * FROM [tile].[fn_GetTilesForObjectByTileNumZoomDepth]( @GeoData , c.Zoom + 1, cX * 2 + 1, cY * 2, @Depth - 1) WHERE @Depth > 0) AS d INSERT INTO @retTiles SELECT d.zoom, dX, dY FROM @currentTile c CROSS APPLY (SELECT * FROM [tile].[fn_GetTilesForObjectByTileNumZoomDepth]( @GeoData , c.Zoom + 1, cX * 2, cY * 2 + 1, @Depth - 1) WHERE @Depth > 0) AS d INSERT INTO @retTiles SELECT d.zoom, dX, dY FROM @currentTile c CROSS APPLY (SELECT * FROM [tile].[fn_GetTilesForObjectByTileNumZoomDepth]( @GeoData , c.Zoom + 1, cX * 2 + 1, cY * 2 + 1, @Depth - 1) WHERE @Depth > 0) AS d INSERT INTO @retTiles SELECT * FROM @currentTile END RETURN END 



1぀のオブゞェクトのタむルを䜜成する必芁がある堎合、タむルのセットは䞀意であるため、タむル番号をすぐにtile.Tileテヌブルに曞き蟌むこずができたす。 耇数のオブゞェクトのゞオメトリが亀差するタむルを圢成するには、異なるオブゞェクト甚に䜜成され、互いに重なり合うタむルを結合する必芁がありたす。
tile.fn_GetTilesByTileNumZoomDepth関数は、指定された深さを考慮しお、オブゞェクトゞオメトリずスケヌルタむルの亀差をチェックしたす。 @Depthパラメヌタヌはスキャンの深さを瀺したす。たずえば、 @ Zoom = 2および@Depth = 1の堎合、スケヌル2タむルがチェックされ、亀差がある堎合、4スケヌル3タむルがチェックされたす。 。 GEOMETRYデヌタタむプをGEOGRAPHYに倉換した埌、亀差チェックを実行する必芁がありたす。GEOGRAPHYデヌタタむプの堎合、4326投圱のゞオメトリポむントのすべおの座暙を考慮しおチェックが実行されるため、これは重芁です。

ポリラむンの䟋




サンクトペテルブルクの䞭心郚ずモスクワの䞭心郚を結ぶ壊れたタむルを入手したいずしたしょう。 長さは玄800 kmです。 壊れた居䜏地を通過したすノノゎロド-ノィシュニヌノォロホック-トベリ。
サンクトペテルブルクからモスクワたでの砎線の圢状
 'LINESTRING( 30.381113 59.971474, 31.26002 58.539215, 34.564158 57.591722, 35.915476 56.876838,37.622242 55.773125)' 


スケヌル3から17のこのゞオメトリの堎合、タむルの合蚈11076を取埗したす。スケヌルのゞオメトリず亀差するタむルの数の分垃を衚1に瀺したす

è¡š1-スケヌルレベルによるタむル数の分垃
スケヌルタむル数
31
42
53
64
77
812
923
1045
1188
12174
13347
14692
151383
162766
175529


3番目ず4番目のスケヌルで埗られたタむルを図1に瀺したす。


図1-タむル3/4/2および4/9/4

各スケヌルに察しお、タむルのサブセットが生成され、各タむルが䟋倖なくチェックされたす。 4〜5のスケヌルでは、オブゞェクトのゞオメトリでGEOMETRY :: STEnvelope関数によっお取埗された長方圢の領域に収たるタむルの数は少なくなりたす。 4番目のスケヌルには合蚈2 ^ 4 * 2 ^ 4 = 256のタむルがありたす。しかし、16番目ず17番目のスケヌルでは、さらに倚くのタむルをチェックする必芁がありたす。 最初の方法で「䞍芁な」チェックを陀倖するず、䜜業がスピヌドアップしたす。 ポむントゞオメトリ POINT を持぀オブゞェクトの堎合、䞡方の方法の効率は同じになりたす。


亀差点チェック




GEOMETRY :: STIntersects関数は、GEOMETRYデヌタ型のSTIntersects関数が平面内の座暙で機胜し、緯床ず経床はデカルト座暙ではないため、オブゞェクトゞオメトリずタむルゞオメトリの亀差を決定できたせん。 したがっお、亀点を確実に刀別するには、 GEOMETRYタむプをGEOGRAPHYタむプに倉換したす。 GEOMETRYデヌタ型ずは異なり、 GEOGRAPHYデヌタ型ではポリゎンリングの向きが必芁であるこずに泚意しおください。 倖茪の座暙は反時蚈回りに移動する必芁がありたす。 内偎のリングボむドの堎合、座暙は時蚈回りにリストする必芁がありたす。 地理の圢成における゚ラヌを回避するために、 GEOMETRY :: MakeValidおよびGEOMETRY :: STUnion関数を䜿甚しお、GEOMETRY型をGEOGRAPHY型に倉換するずきに正しい座暙シヌケンスを取埗したす。 地理を䜜成する堎合、パラメヌタヌSRID = 4326を指定したす。これは、座暙に察するすべおの操䜜が球面システムで実行されるこずを意味したす 。

この段階で、タむルのリスト、぀たり3぀の列を持぀テヌブル、Z、X、Y、 mapnik 再レンダリングプログラムを䜿甚しお、さらに䜜業を行うこずができたす。これにより、オブゞェクトの画像でタむルを圢成できたす。 Microsoft SQL Serverデヌタベヌスぞのmapnikアクセスを敎理するには、ある皋床の努力が必芁です。 mapnikでタむルを生成する準備には、次の手順が含たれたす。
•レンダリング甚のオブゞェクトのスタむルを宣蚀したす。
•オブゞェクトのゞオメトリのデヌタ゜ヌスの説明。
•すべおのタむルが連続しお生成されないように、レンダリング甚のタむルのリストを含むテヌブルを指定したす。
MS SQL Server 2008デヌタベヌス内にタむルを生成したすこれを行うには、デヌタベヌスに栌玍されおいるゞオメトリを操䜜するためのいく぀かのCLR関数を実装する必芁がありたす。 必芁ずなる䞻な機胜は以䞋のずおりです。
  • tile.IconTile、
  • tile.ShapeTile、
  • tile.TileAgg、
  • tile.SaveToFolderByZoomXY
。

タむル画像を保存するためのテヌブル




図2は、レンダリング甚のタむルのリストが保存されおいるテヌブル構造を瀺しおいたす。 これらのテヌブルの[デヌタ]フィヌルドには、タむル䞊に萜䞋するオブゞェクトの画像を含むPNG画像が保存されたす。 テヌブルに倚数の画像を保存しお凊理するず、パフォヌマンスに圱響する可胜性がありたす。 このタスクの堎合、より適切なオプションは、テヌブル内に圢成された各オブゞェクトのタむルのリストを䜿甚しおデヌタベヌス倖にタむルむメヌゞを䜜成し、ファむルシステムに保存するこずです。


図2-タむルのリストを保存するためのテヌブル

タむルにアむコンを配眮する




タむル䞊にアむコンを配眮するためのアルゎリズムを分析しおみたしょうゞオメトリの皮類POINT。
あるオブゞェクトの緯床ず経床があり、珟圚のスケヌルのタむルのリストがあり、その䞊にアむコンが重ねられおいたす。 タむルのリストの圢成は以前に説明されおいたす。 タむル䞊のアむコンの䜍眮の蚈算は、次のアクションで構成されたす。
1.最初に、緯床ず経床を絶察ピクセル座暙に倉換したす。
2.次に、䜿甚可胜なリストの各タむルに぀いお、珟圚のスケヌルで、巊䞊隅の絶察ピクセル座暙を蚈算したす。 タむルの巊䞊のピクセルpixXtile、pixYtileの座暙は、タむル䜍眮のx番号ずy番号にそのサむズこの堎合は256ピクセルを掛けお蚈算されたす。

3.オブゞェクトの絶察ピクセル座暙ずタむルの巊䞊隅の絶察ピクセル座暙の差は、GetPixelXOnTileおよびGetPixelXOnTile関数で決定されたす。 この違いは、タむル䞊のアむコンの䞭心の盞察ピクセル座暙です。
4.タむル䞊にアむコンを描画するには、タむル䞊の描画領域の境界をピクセル単䜍で取埗する必芁があり、そこに挿入が行われたす。 タむル䞊のオブゞェクトの盞察ピクセル座暙は、前の手順で取埗されたした。 これで、アむコンのサむズによっお、挿入する長方圢の領域の境界が決たりたす。
5.タむルにアむコンを描画したす。

タむルにアむコンを配眮するCLR SQL関数
 [Microsoft.SqlServer.Server.SqlFunction] public static SqlBinary IconTile(SqlBinary image, SqlInt32 zoom, SqlDouble Lon, SqlDouble Lat, SqlInt32 xTile, SqlInt32 yTile, SqlDouble scale) { SqlBinary result = null; using (Icon2TileRendering paster = new Icon2TileRendering()) { using (MemoryStream ms = new MemoryStream()) { ms.Write(image.Value, 0, image.Length); SetBeginPosition(ms); paster.PasteFromStreamScaledImageToTile((int)zoom, (double)Lon, (double)Lat, (int)xTile, (int)yTile, (double)scale, ms); result = paster.GetBytes(); } } return result; } 


描画領域の境界線を取埗したす
  #region [Pixel Position Calculation] Rectangle GetTargetBound(int zoom, double Lon, double Lat, int xTile, int yTile, int width, int height) { int xPix = _conv.FromLongitudeToXPixel(Lon, zoom); int yPix = _conv.FromLatitudeToYPixel(Lat, zoom); int xPos = xPix - xTile * TILE_SIZE; int yPos = yPix - yTile * TILE_SIZE; int halfWidth = width / 2; int halfHeight = height / 2; return new Rectangle(xPos - halfWidth, yPos - halfHeight, width, height } int GetPixelXOnTile(int zoom, double Lon, int xTile) { return _conv.FromLongitudeToXPixel(Lon, zoom) - xTile * TILE_SIZE; } int GetPixelYOnTile(int zoom, double Lat, int yTile) { return _conv.FromLatitudeToYPixel(Lat, zoom) - yTile * TILE_SIZE; } #endregion [Pixel Position Calculation] 


アむコンをタむルにコピヌ
 /// <summary> ///     /// </summary> /// <param name="zoom"></param> /// <param name="Lon"></param> /// <param name="Lat"></param> /// <param name="xTile"></param> /// <param name="yTile"></param> /// <param name="iconImage"></param> public void PasteImageToTileByLatLon(int zoom, double Lon, double Lat, int xTile, int yTile, Bitmap iconImage) { int width = iconImage.Width; int height = iconImage.Height; CopyRegionIntoImage(iconImage, new Rectangle(0, 0, width, height), GetTargetBound(zoom, Lon, Lat, xTile, yTile, width, height)); } 



タむルの関連付け




耇数のオブゞェクトのアむコンを同じタむルに重ねるこずができたす。 すべおのオブゞェクトでタむルを取埗するには、たず各オブゞェクトのタむルを䜜成しおから、それらを1぀にマヌゞしたす。 このような゜リュヌションは、デヌタベヌステヌブルの行をグルヌプ化するこずで実装できたす;このために、CLR関数集蚈tile.TileAggが䜜成され 、タむルを1぀に結合したした。 この゜リュヌションにはマむナスが1぀ありたす。タむルで切り詰められた各オブゞェクトに察しお、タむルむメヌゞを栌玍するBINARYフィヌルドを持぀個別のレコヌドを栌玍し、このオブゞェクトのみが遞択されるため、倧量のメモリが必芁になるためです。 より適切な解決策は、タむルの1぀のむンスタンスを䜿甚し、タむル䞊にあるオブゞェクトのすべおのアむコンを垞に衚瀺するこずです。 したがっお、メモリの消費が少なくなりたす。 この堎合、グルヌプ化するものは䜕もありたせん。 グルヌプ化を䜿甚したい。
テヌブルにタむルの䜍眮ずアむコンに描かれたタむル画像を入力する
 CREATE PROC [tile].[FillObjectTilesIntersection]( @StartZoom INT, @EndZoom INT) AS BEGIN DECLARE @CurrentZoom INT SET @CurrentZoom = @StartZoom WHILE @CurrentZoom <= @EndZoom BEGIN INSERT INTO tile.Tile (X,Y,Data,Zoom) SELECT t.TileX,t.TileY, [tile].[TileAgg] (tile.IconTile(i.Data, @CurrentZoom,o.Longitude,o.Latitude,t.tileX,t.tileY, 1.0) ),@CurrentZoom AS Zoom FROM tile.Shape o INNER JOIN tile.[Image] i ON i.ImageID = o.ImageID CROSS APPLY tile.fn_FetchObjectTiles(tile.GetIconBoundForZoom(o.Longitude, o.Latitude, 64, 64, @CurrentZoom, 0),@CurrentZoom) t WHERE o.TypeID = @TypeID GROUP BY t.TileX,t.TileY SET @CurrentZoom = @CurrentZoom + 1 END END 


オブゞェクトの゜ヌスずしお、オブゞェクトの座暙ず、 Binaryタむプのフィヌルドのtile.Imageテヌブルに保存されおいるアむコンの画像の識別子を含むtile.Objectテヌブルを䜿甚したす。
アむコンを経床30.381113および緯床59.971474に配眮しおタむル3/4/2および4/9/4を圢成するためのスクリプト
 DECLARE @Image VARBINARY(MAX) SELECT TOP (1) @image = ( SELECT * FROM OPENROWSET(BULK N'd:\media\icons\pin_orange.png', SINGLE_BLOB) As tile) SELECT [tile].[SaveToFolderByZoomXY]([tile].[IconTile](@image, 3,30.381113, 59.971474, 4, 2, 1.0), N'D:\Tiles\',3,4,2) SELECT [tile].[SaveToFolderByZoomXY]([tile].[IconTile](@image, 4,30.381113, 59.971474, 9, 4, 1.0), N'D:\Tiles\',4,9,4) 


図3-受信したタむルずアむコン




タむル䞊のゞオメトリの描画




ポリラむンPOLYLINE、MULTIPOLYLINEの堎合、タむルゞオメトリをポリラむンゞオメトリず組み合わせお、タむル領域倖のポリラむンの䞀郚が陀倖されるようにしたす。 茪郭ず圱付き領域を決定するアルゎリズムは、領域を持぀ゞオメトリ、぀たりPOLYGONたたはMULTIPOLYGON 、 POLYGONたたはMULTYPOLYGONを含むGEOMETRYCOLLECTIONに適甚できたす。 このアルゎリズムはShapeToTileRenderingクラスに実装されおおり、次の手順が含たれおいたす。
1.ゞオメトリの座暙緯床、経床は、緯床、経床をピクセルPSG3857Googleプロゞェクションに倉換する匏を䜿甚しおスケヌルを考慮しおピクセル座暙に倉換され、結果のゞオメトリの各座暙からタヌゲットタむルの巊䞊のピクセルの座暙を匕きたす。 いわゆるゞオメトリ A を取埗したす。 これらのアクションは、関数ConvertToZoomedPixelZeroedByTileGeometrypoly、Z、X、Yで実装されたす
2.タむルのゞオメトリ B は、スケヌルを考慮しおピクセル座暙で圢成されたす

3.タむルのピクセルゞオメトリ B ずオブゞェクトのゞオメトリ A の亀差 STIntersection によっお取埗された圢成枈みゞオメトリ C 
4.ゞオメトリの茪郭 C ずタむルのゞオメトリの茪郭 B の亀差の結果ずしお埗られたゞオメトリ D が圢成されたす。タむルの境界に沿っお、タむル内のポリゎンの想定シェヌディング゚リアに接するラむンを取埗したす。 枛算の結果ずしお埗られたゞオメトリ E は、 .STDifferenceother_geometry関数を䜿甚しお圢成されたす

5.ゞオメトリ E は描画甚の茪郭で、関数
6.ゞオメトリポリゎンが塗り぀ぶされたす C -塗り぀ぶされた領域が取埗されたす
7.ポリゎンの E 茪郭のゞオメトリは、タむルの境界ずの亀差を陀去した埌に描画されたす
8.珟圚のオブゞェクトのすべおのタむルに぀いお手順1〜7を繰り返し、タむルに保存したす。TileOverlapテヌブル
3 15- X 19144 Y 9524. T-SQL . , :
  SELECT [tile].[GetTileBounds](15,19144,9524).ToString() 

:
  'POLYGON ((30.322265625 59.955010262062061, 30.322265625 59.949509172252277, 30.333251953125 59.949509172252277, 30.333251953125 59.955010262062061, 30.322265625 59.955010262062061))' 


, . . . , , 6367 . , ( ), , . , . , 360 90 , . , c , . :
 SELECT [tile].[fn_GetCircleSegment](30.3277587890625, 59.952259717159905,0,360,440,90) 


 CREATE FUNCTION [tile].[fn_ GetCircleSegment] (@X float, @Y float, @azimuth float, @angle float, @distance float, @step FLOAT) RETURNS geometry WITH EXEC AS CALLER AS BEGIN IF @X IS NULL OR @Y IS NULL OR @azimuth IS NULL OR ISNULL(@angle, 0) = 0 OR ISNULL(@distance, 0) <= 0 RETURN NULL DECLARE @sectorStepAngle FLOAT SET @sectorStepAngle = @step IF ABS(@angle) > 360 SET @angle = 360 DECLARE @pointsStr VARCHAR(MAX) DECLARE @firstPointsStr VARCHAR(MAX) DECLARE @earthRadius FLOAT DECLARE @lat FLOAT DECLARE @lon FLOAT DECLARE @d FLOAT IF ABS(@angle) < 360 SET @pointsStr = LTRIM(STR(@X, 25, 16)) + ' ' + LTRIM(STR(@Y, 25, 16)) ELSE SET @pointsStr = '' SET @earthRadius = 6367 SET @lat = RADIANS(@Y) SET @lon = RADIANS(@X) SET @d = (@distance / 1000) / @earthRadius DECLARE @angleStart FLOAT DECLARE @angleEnd FLOAT SET @angleStart = @azimuth - @angle / 2; SET @angleEnd = @azimuth + @angle / 2; DECLARE @pointsCount INT SET @pointsCount = FLOOR(@angle / @sectorStepAngle) DECLARE @brng FLOAT DECLARE @latRadians FLOAT DECLARE @lngRadians FLOAT DECLARE @ptX FLOAT DECLARE @ptY FLOAT DECLARE @i INT SET @i = 0 DECLARE @addPrefix TINYINT IF ABS(@angle) < 360 SET @addPrefix = 1 ELSE SET @addPrefix = 0 WHILE @i <= @pointsCount BEGIN SET @brng = RADIANS(@angleStart + @i * @sectorStepAngle); SET @latRadians = ASIN(SIN(@lat) * COS(@d) + COS(@lat) * SIN(@d) * COS(@brng)); SET @lngRadians = @lon + ATN2(SIN(@brng) * SIN(@d) * COS(@lat), COS(@d) - SIN(@lat) * SIN(@latRadians)); SET @ptX = 180.0 * @lngRadians / PI(); SET @ptY = 180.0 * @latRadians / PI(); IF @addPrefix = 1 BEGIN SET @pointsStr += ', ' + LTRIM(STR(@ptX, 25, 16)) + ' ' + LTRIM(STR(@ptY, 25, 16)) END ELSE BEGIN SET @pointsStr += LTRIM(STR(@ptX, 25, 16)) + ' ' + LTRIM(STR(@ptY, 25, 16)) SET @addPrefix = 1 END IF @i = 0 SET @firstPointsStr = LTRIM(STR(@ptX, 25, 16)) + ' ' + LTRIM(STR(@ptY, 25, 16)) IF @i = @pointsCount AND (@angleStart + @pointsCount * @sectorStepAngle) < @angleEnd BEGIN SET @brng = RADIANS(@angleEnd); SET @latRadians = ASIN(SIN(@lat) * COS(@d) + COS(@lat) * SIN(@d) * COS(@brng)); SET @lngRadians = @lon + ATN2(SIN(@brng) * SIN(@d) * COS(@lat), COS(@d) - SIN(@lat) * SIN(@latRadians)); SET @ptX = 180.0 * @lngRadians / PI(); SET @ptY = 180.0 * @latRadians / PI(); SET @pointsStr = @pointsStr + ', ' + LTRIM(STR(@ptX, 25, 16)) + ' ' + LTRIM(STR(@ptY, 25, 16)) END SET @i = @i + 1 END IF ABS(@angle) < 360 SET @pointsStr += ', ' + LTRIM(STR(@X, 25, 16)) + ' ' + LTRIM(STR(@Y, 25, 16)) ELSE SET @pointsStr += ', ' + @firstPointsStr RETURN geometry::STPolyFromText('POLYGON ((' + @pointsStr + '))', 0) END GO 



, CLR , . .
CLR
 /// <summary> ///    /// </summary> /// <param name="longitude"></param> /// <param name="latitude"></param> /// <param name="azimuth"></param> /// <param name="angle"></param> /// <param name="radius"></param> /// <returns></returns> [Microsoft.SqlServer.Server.SqlFunction] public static SqlGeometry DrawGeoSpatialSectorVarAngle(SqlDouble longitude, SqlDouble latitude, SqlDouble azimuth, SqlDouble angle, SqlDouble radius, SqlDouble stepAngle) { if (longitude == SqlDouble.Null || latitude == SqlDouble.Null || azimuth == SqlDouble.Null || angle == SqlDouble.Null || radius == SqlDouble.Null || radius == 0 || angle == 0) return SqlGeometry.Parse("GEOMETRYCOLLECTION EMPTY"); SqlGeometryBuilder builder = new SqlGeometryBuilder(); builder.SetSrid(0); builder.BeginGeometry(OpenGisGeometryType.Polygon); double firstPointLon; double firstPointLat; double sectorStepAngle = (double) stepAngle; const double earthRadius = 6367.0; double lat = (double) latitude; double lon = (double) longitude; double azim = (double) azimuth; double ang = (double) angle; double piRad = (Math.PI/180.0); double tLat = piRad*lat; double tLon = piRad*lon; double distkm = ((double) radius/1000)/earthRadius; double angleStart = azim - ang/2; double angleEnd = azim + ang/2; var _angle = Math.Abs(ang); if (_angle > 360.0) { angle = 360.0; } int pointCount = (int) Math.Floor(ang/sectorStepAngle); double brng; double latRadians; double lngRadians; double ptX; double ptY; int i = 0; if (angle < 360.0) { builder.BeginFigure(lon, lat); firstPointLon = lon; firstPointLat = lat; } else { brng = piRad*(angleStart); latRadians = Math.Asin(Math.Sin(tLat)*Math.Cos(distkm) + Math.Cos(tLat)*Math.Sin(distkm)*Math.Cos(brng)); lngRadians = tLon + Math.Atan2(Math.Sin(brng)*Math.Sin(distkm)*Math.Cos(tLat), Math.Cos(distkm) - Math.Sin(tLat)*Math.Sin(latRadians)); ptX = 180.0*lngRadians/Math.PI; ptY = 180.0*latRadians/Math.PI; builder.BeginFigure(ptX, ptY); firstPointLon = ptX; firstPointLat = ptY; } while (i <= pointCount) { brng = piRad*(angleStart + i*sectorStepAngle); latRadians = Math.Asin(Math.Sin(tLat)*Math.Cos(distkm) + Math.Cos(tLat)*Math.Sin(distkm)*Math.Cos(brng)); lngRadians = tLon + Math.Atan2(Math.Sin(brng)*Math.Sin(distkm)*Math.Cos(tLat), Math.Cos(distkm) - Math.Sin(tLat)*Math.Sin(latRadians)); ptX = 180.0*lngRadians/Math.PI; ptY = 180.0*latRadians/Math.PI; builder.AddLine(ptX, ptY); i = i + 1; } if (((angleStart + pointCount * sectorStepAngle) < angleEnd)) { brng = piRad * (angleEnd); latRadians = Math.Asin(Math.Sin(tLat) * Math.Cos(distkm) + Math.Cos(tLat) * Math.Sin(distkm) * Math.Cos(brng)); lngRadians = tLon + Math.Atan2(Math.Sin(brng) * Math.Sin(distkm) * Math.Cos(tLat), Math.Cos(distkm) - Math.Sin(tLat) * Math.Sin(latRadians)); ptX = 180.0 * lngRadians / Math.PI; ptY = 180.0 * latRadians / Math.PI; builder.AddLine(ptX, ptY); } builder.AddLine(firstPointLon, firstPointLat); builder.EndFigure(); builder.EndGeometry(); return builder.ConstructedGeometry; } 



()
 DECLARE @bbox GEOMETRY DECLARE @octagon GEOMETRY SELECT @bbox = [tile].[GetTileBounds](15,19144,9524), @octagon = [tile].[fn_GetCircleSegment](30.3277587890625, 59.952259717159905,0,360,440,90) 


30.3277587890625, 59.952259717159905 – ;

, :
 SELECT @bbox.STIntersection(@octagon) 

:
 'POLYGON ((30.3253442162734 59.949509172234684, 30.3301733618516 59.949509172234684, 30.333251953125 59.9510505967796, 30.333251953125 59.953468509045528, 30.330173073498937 59.955010262085125, 30.325344504626063 59.955010262085125, 30.322265625 59.953468509045528, 30.322265625 59.9510505967796, 30.3253442162734 59.949509172234684))' 



, :
X Y
 SELECT [tile].[GetPixelXPosFromLongitude](30.3253442162734,15), [tile].[GetPixelYPosFromLatitude](59.949509172234684,15) , [tile].[GetPixelXPosFromLongitude](30.3301733618516,15), [tile].[GetPixelYPosFromLatitude]( 59.949509172234684,15) , [tile].[GetPixelXPosFromLongitude](30.333251953125,15), [tile].[GetPixelYPosFromLatitude]( 59.9510505967796,15) , [tile].[GetPixelXPosFromLongitude](30.333251953125,15), [tile].[GetPixelYPosFromLatitude]( 59.953468509045528,15) , [tile].[GetPixelXPosFromLongitude](30.330173073498937,15), [tile].[GetPixelYPosFromLatitude]( 59.955010262085125,15) , [tile].[GetPixelXPosFromLongitude](30.325344504626063,15), [tile].[GetPixelYPosFromLatitude]( 59.955010262085125,15) ,[tile].[GetPixelXPosFromLongitude](30.322265625,15), [tile].[GetPixelYPosFromLatitude]( 59.953468509045528,15) , [tile].[GetPixelXPosFromLongitude](30.322265625,15), [tile].[GetPixelYPosFromLatitude]( 59.9510505967796,15) , [tile].[GetPixelXPosFromLongitude](30.3253442162734,15), [tile].[GetPixelYPosFromLatitude]( 59.949509172234684,15) 


X 15-Y 15-
30.325344216273459.94950917223468449009362438400
30.330173361851659.94950917223468449010482438400
30.33325195312559.951050596779649011202438328
30.33325195312559.95346850904552849011202438216
30.33017307349893759.95501026208512549010482438144
30.32534450462606359.95501026208512549009362438144
30.32226562559.95346850904552849008642438216
30.32226562559.951050596779649008642438328
30.325344216273459.94950917223468449009362438400


:
 SELECT GEOMETRY::STGeomFromText('LINESTRING(4900936 2438400, 4901048 2438400, 4901120 2438328, 4901120 2438216, 4901048 2438144, 4900936 2438144, 4900864 2438216, 4900864 2438328, 4900936 2438400 )',0) 



( ) 3 , , 4 .

4 –

( D ) , , , 5 , ( ) ( D ), 5.
.

( E ):
 SELECT GEOMETRY::STGeomFromText('MULTILINESTRING((4901048 2438400, 4901120 2438328),( 4901120 2438216, 4901048 2438144),( 4900936 2438144, 4900864 2438216), (4900864 2438328, 4900936 2438400) )',0) 



5 – ( )

T-SQL PNG Z/X/Y. .
 DECLARE @bbox GEOMETRY DECLARE @rhomb GEOMETRY DECLARE @image VARBINARY(MAX) SELECT @bbox = [tile].[GetTileBounds](15,19144,9524), @rhomb = [tile].[fn_GetSector](30.3277587890625, 59.952259717159905,0,360,440,90) SET @image = [tile].[ShapeTile]( @octagon,15,19144,9524,'4400B050','9601B41E',3) SELECT[tile].[SaveToFolderByZoomXY](@image,'d:/tiles',15,19144,9524) SET @image = [tile].[ShapeTile]( @octagon,15,19143,9524,'4400B050','9601B41E',3) SELECT[tile].[SaveToFolderByZoomXY](@image,'d:/tiles',15,19143,9524) SET @image = [tile].[ShapeTile]( @octagon,15,19145,9524,'4400B050','9601B41E',3) SELECT[tile].[SaveToFolderByZoomXY](@image,'d:/tiles',15,19145,9524) SET @image = [tile].[ShapeTile]( @octagon,15,19144,9523,'4400B050','9601B41E',3) SELECT[tile].[SaveToFolderByZoomXY](@image,'d:/tiles',15,19144,9523) SET @image = [tile].[ShapeTile]( @octagon,15,19144,9525,'4400B050','9601B41E',3) SELECT[tile].[SaveToFolderByZoomXY](@image,'d:/tiles',15,19144,9525) 


PNG :



DrawPartObjectShapeOnTile() ShapeToTileRendering :
 /// <summary> ///      /// </summary> /// <param name="shape">       </param> /// <param name="X">X  </param> /// <param name="Y">Y  </param> /// <param name="Zoom">  </param> /// <param name="argbFill">    ARGB</param> /// <param name="argbStroke"> </param> /// <param name="strokeWidth"> </param> public void DrawPartObjectShapeOnTile(SqlGeometry shape, int X, int Y, int Zoom, string argbFill, string argbStroke, int strokeWidth) { PasteShapeOnTile(CreateColor(argbFill), CreateColor(argbStroke), strokeWidth, CutPolygonByZoomedPixelZeroTile(shape, X, Y, Zoom)); } 


PasteShapeOnTile() .

 private void PasteShapeOnTile(Color fillcolor, Color strokecolor, int width, List<SqlGeometry> geom) { SqlGeometry shape = geom[0]; int geomnum = (int) shape.STNumGeometries(); SqlGeometry stroke = null; SqlGeometry ring; int intnum; if (geom != null) switch (GetOpenGisGeometryType(shape)) { case OpenGisGeometryType.LineString: case OpenGisGeometryType.MultiLineString: DrawMultiLineStringBordered2(shape, fillcolor, strokecolor, width, 1); break; case OpenGisGeometryType.Polygon: intnum = (int) shape.STNumInteriorRing(); ring = shape.STExteriorRing(); // 1.      FillPolygonOnTile(fillcolor, ring.ToPointsArray()); // 2.    if (geomnum >= 1) stroke = geom[1]; for (int i = 1; i <= intnum; i++) { FillTransparentPolygonOnTile(shape.STInteriorRingN(i).ToPointsArray()); } // 3.   if (geom.Count > 1) { stroke = geom[1]; DrawContourOnTile(stroke, strokecolor, width); } break; case OpenGisGeometryType.MultiPolygon: break; } } 


3-7, , CutPolygonByZoomedPixelZeroTile() , .
 /// <summary> ///            /// </summary> /// <param name="poly"></param> /// <param name="X"></param> /// <param name="Y"></param> /// <param name="Z"></param> /// <returns>          </returns> private List<SqlGeometry> CutPolygonByZoomedPixelZeroTile(SqlGeometry poly, int X, int Y, int Z) { return CutZoomedPixelPolygonByZeroTile(_parser.ConvertToZoomedPixelZeroedByTileGeometry(poly,Z,X,Y); } 

GeometryParser «» — X, Y. , :

 /// <summary ///            /// </summary> /// <param name="shape">    </param> /// <param name="zoom">  </param> /// <param name="tileX">   </param> /// <param name="tileY">Y  </param> /// <returns>        </returns> public SqlGeometry ConvertToZoomedPixelZeroedByTileGeometry(SqlGeometry shape,int zoom, int tileX,int tileY) { return CreateGeometryFromZoomedPixelInfo (ConvertToGeometryZoomedPixelsZeroTileShiftedInfo( GetGeometryInfo(shape), zoom, tileX, tileY)); } /// <summary ///            /// </summary> /// <param name="info">   </param> /// <param name="zoom">  </param> /// <param name="x">   </param> /// <param name="y">Y  </param> /// <returns>       </returns> private GeometryZoomedPixelsInfo ConvertToGeometryZoomedPixelsZeroTileShiftedInfo (GeometryInstanceInfo info, int zoom, int x, int y) { int tilezeroshiftX = x*TILE_SIZE; int tilezeroshiftY = y*TILE_SIZE; var result = new GeometryZoomedPixelsInfo(); var pixelCoordsListList = new List<List<GeometryPixelCoords>>(); var geomPixCoordsList = new List<GeometryPixelCoords>(); var coords = new GeometryPixelCoords {InnerRing = false}; OpenGisGeometryType type = info.ShapeType; result.ShapeType = type; switch (type) { case OpenGisGeometryType.Point: PointF[] geopoints = info.Points[0][0].PointList; coords.PixelCoordList = new[] {new Point{X = _conv.FromLongitudeToXPixel(geopoints[0].X, zoom) - tilezeroshiftX, Y = _conv.FromLatitudeToYPixel(geopoints[0].Y, zoom) - tilezeroshiftY } }; geomPixCoordsList.Add(coords); pixelCoordsListList.Add(geomPixCoordsList); break; case OpenGisGeometryType.LineString: coords.PixelCoordList = GetPixelCoordsShifted( info.Points[0][0].PointList, zoom, tilezeroshiftX, tilezeroshiftY); geomPixCoordsList.Add(coords); pixelCoordsListList.Add(geomPixCoordsList); break; case OpenGisGeometryType.Polygon: foreach (var list in info.Points) foreach (GeometryPointSequence pointseq in list) { coords.PixelCoordList = GetPixelCoordsShifted(pointseq.PointList, zoom, tilezeroshiftX, tilezeroshiftY); coords.InnerRing = pointseq.InnerRing; geomPixCoordsList.Add(coords); } pixelCoordsListList.Add(geomPixCoordsList); break; case OpenGisGeometryType.MultiPoint: case OpenGisGeometryType.MultiLineString: case OpenGisGeometryType.MultiPolygon: pixelCoordsListList = GetGeometryPixelCoordsShifted(info.Points, zoom, tilezeroshiftX, tilezeroshiftY); break; case OpenGisGeometryType.GeometryCollection: GeometryInstanceInfo[] geomColl = info.GeometryInstanceInfoCollection; int n = info.GeometryInstanceInfoCollection.Length; var geomPixZoomInfoCollection = new GeometryZoomedPixelsInfo[n]; for (int i = 0; i < n; i++) { var geom = new GeometryZoomedPixelsInfo(); geom.ShapeType = geomColl[i].ShapeType; geom.Points = GetGeometryPixelCoordsShifted(geomColl[i].Points, zoom, tilezeroshiftX, tilezeroshiftY); geomPixZoomInfoCollection[i] = geom; } result.GeometryInstanceInfoCollection = geomPixZoomInfoCollection; break; } if (type != OpenGisGeometryType.GeometryCollection) result.Points = pixelCoordsListList; return result; } 

ShapeToTileRendering CutZoomedPixelPolygonByZeroTile() . . , poly , .

  private List<SqlGeometry> CutZoomedPixelPolygonByZeroTile(SqlGeometry poly, int X, int Y) { List<SqlGeometry> result = new List<SqlGeometry>(); SqlGeometry stroke = null; SqlGeometry contour; SqlGeometry tileLineString; SqlGeometry tobecut; SqlGeometry tile = _conv.GetTilePixelBound(0, 0, 1); var tiled = poly.STIntersection(tile); result.Add(tiled); switch (GetOpenGisGeometryType(tiled)) { case OpenGisGeometryType.Polygon: //         MULTILINESTRING contour = PolygonToMultiLineString(tiled); //        tileLineString = tile.ToLineString(); tobecut = contour.STIntersection(tileLineString); stroke = contour.STDifference(tobecut); break; case OpenGisGeometryType.MultiPolygon: //         MULTILINESTRING contour = MultiPolygonToMultiLineString(tiled); //        tileLineString = tile.ToLineString(); tobecut = contour.STIntersection(tileLineString); stroke = contour.STDifference(tobecut); break; } result.Add(stroke); return result; } 


. tile.FillShapeTiles , @GeoData @FolderPath .
CLR :
。
BitmapFunctions SQL CLR SqlBitmapOperation :
ShapeTile() PNG xTile, yTile:

ShapeTile()
 [SqlFunction] public static SqlBinary ShapeTile(SqlGeometry shape, SqlInt32 zoom, SqlInt32 xTile, SqlInt32 yTile, SqlString argbFill,SqlString argbStroke,SqlInt32 strokeWidth) { SqlBinary result = null; using (ShapeToTileRendering paster = new ShapeToTileRendering()) { using (MemoryStream ms = new MemoryStream()) { try { paster.DrawPartObjectShapeOnTile(shape, (int) xTile, (int) yTile, (int) zoom, argbFill.ToString(), argbStroke.ToString(), (int) strokeWidth); result = paster.GetBytes(); } catch (System.Exception ex) { string innerMessage = ex.InnerException.Message; throw new Exception(string.Format("zoom: {1}; X:{2}; Y:{3} {0} , inner: {4}", shape, zoom, xTile,yTile, innerMessage)); } return result; } } } 



SqlBitmapOperation TileRendering .
.NET TileRendering .NET :
  • システム
  • Microsoft.SqlServer.Types
  • System.Drawing


msdn.microsoft.com/en-us/library/ms345099.aspx
SqlBitmapOperation TileRendering , , :
 CREATE ASSEMBLY [Microsoft.SqlServer.Types] AUTHORIZATION [dbo] FROM 'd:\SQLCLR\BIN\TileRendering\Microsoft.SqlServer.Types.dll' WITH PERMISSION_SET = UNSAFE GO CREATE ASSEMBLY [System.Drawing] AUTHORIZATION [dbo] FROM 'd:\SQLCLR\BIN\TileRendering\ System.Drawing.dll' WITH PERMISSION_SET = UNSAFE GO CREATE ASSEMBLY [TileRendering] AUTHORIZATION [dbo] FROM 'd:\SQLCLR\BIN\TileRendering\TileRendering.dll' WITH PERMISSION_SET = UNSAFE GO CREATE ASSEMBLY nQuant.Core FROM 'd:\SQLCLR\BIN\TileRendering\ nQuant.Core.dll' WITH PERMISSION_SET = UNSAFE GO CREATE ASSEMBLY SqlBitmapOperation FROM 'd:\SQLCLR\BIN\TileRendering\SqlBitmapOperation.dll' WITH PERMISSION_SET = UNSAFE GO 


SqlBitmapOperation nQuant.Core . PNG 8 .

SqlGeometry Microsoft.SqlServer.Types , Microsoft.SqlServer.Types .
System.Drawing – GDI+ c , EXTERNAL_ACCESS , Sytem.Drawing . , EXTERNAL_ACCESS UNSAFE T-SQL :
 ALTER DATABASE [dataBaseName] SET TRUSTWORTHY ON; 


CLR , , :
 CREATE AGGREGATE [tile].[TileAgg] (@Value [varbinary](max)) RETURNS[varbinary](max) EXTERNAL NAME [SqlBitmapOperation].[TileAgg] GO CREATE AGGREGATE [tile].[IconTileAgg] (@Value [varbinary](max), @PixelX [int], @PixelY [int]) RETURNS[varbinary](max) EXTERNAL NAME [SqlBitmapOperation].[IconTileAgg] GO CREATE FUNCTION [tile].[IconTile](@image [varbinary](max), @zoom [int], @Lon [float], @Lat [float], @xTile [int], @yTile [int], @scale [float]) RETURNS [varbinary](max) WITH EXECUTE AS CALLER AS EXTERNAL NAME [SqlBitmapOperation].[BitmapFunctions].[IconTile] GO --ShapeTile(SqlGeometry shape, SqlInt32 zoom, SqlInt32 xTile, SqlInt32 yTile, SqlString argbFill,SqlString argbStroke,SqlInt32 strokeWidth) CREATE FUNCTION [tile].[ShapeTile](@shape GEOMETRY, @zoom [int], @xTile [int], @yTile [int], @argbFill NVARCHAR(10),@argbStroke NVARCHAR(10), @strokeWidth INT) RETURNS [varbinary](max) WITH EXECUTE AS CALLER AS EXTERNAL NAME [SqlBitmapOperation].[BitmapFunctions].[ShapeTile] GO --SaveToFolderByZoomXY(SqlBinary image, SqlString rootFolderPath, SqlInt32 Zoom, SqlInt32 X,SqlInt32 Y) CREATE FUNCTION tile.SaveToFolderByZoomXY(@image VARBINARY(MAX),@rootFolderPat NVARCHAR(512) , @Zoom [int], @xTile [int], @yTile [int]) RETURNS BIT WITH EXECUTE AS CALLER AS EXTERNAL NAME [SqlBitmapOperation].[BitmapFunctions].[SaveToFolderByZoomXY] GO 


ShapeToTileRendering . 4326 . GeometryParser, PSG3857, . PastShapeOnTile , geom . 256 .
  void PasteShapeOnTile(Color fillcolor,Color strokecolor, int width, List<SqlGeometry> geom) { SqlGeometry shape = geom[0]; int geomnum = (int)shape.STNumGeometries(); SqlGeometry stroke = null; SqlGeometry ring; int intnum; if (geom != null) switch (GetOpenGisGeometryType(shape)) { case OpenGisGeometryType.LineString: case OpenGisGeometryType.MultiLineString: DrawMultiLineStringBordered2(shape, fillcolor, strokecolor, width, 1); break; case OpenGisGeometryType.Polygon: intnum = (int)shape.STNumInteriorRing(); ring = shape.STExteriorRing(); // 1.      FillPolygonOnTile(fillcolor, ring.ToPointsArray()); // 2.    if (geomnum >= 1) stroke = geom[1]; for (int i = 1; i <= intnum; i++) { FillTransparentPolygonOnTile(shape.STInteriorRingN(i).ToPointsArray()); } // 3.   if (geom.Count > 1) { stroke = geom[1]; DrawContourOnTile(stroke, strokecolor, width); } break; case OpenGisGeometryType.MultiPolygon: break; } } 


tile.FillShapeTiles .
tile.FillShapeTiles
 CREATE PROC tile.FillShapeTiles @GeoData GEOMETRY, @fillArgb VARCHAR(20),@strokeArgb VARCHAR(20), @FolderPath NVARCHAR(20), @EndZoom INT = 17, @StartZoom INT = 4, @Thickness INT = 2 AS BEGIN IF @EndZoom < @StartZoom OR @GeoData IS NULL RETURN INSERT INTO tile.tile (Zoom, X,Y,Data) SELECT t.Zoom, t.TileX AS X,t.TileY AS Y, tile.ShapeTile(@GeoData, t.Zoom, t.TileX, t.TileY, @fillArgb, @strokeArgb ,@Thickness) AS Data FROM (SELECT * FROM tile.fn_FetchGeometryTilesZoomDepth(@GeoData,@StartZoom, @EndZoom - @StartZoom)) t SELECT tile.SaveToFolderByZoomXY (Data, @FolderPath ,Zoom,X,Y) FROM tile.Tile END 



, 100 000 , . . , CLR .

tile.FillShapeTilesIntersection() CLR tile.ShapeTile() .PNG , . CLR . , CLR tile.TileAgg(@Data VARBINARY(MAX)) , .PNG , VABINARY(MAX).
CLR :
  • Init();
  • Accumulate(value);
  • Merge(Agg);
  • Terminate()

SQL CLR
 //------------------------------------------------------------------------------ // <copyright file="CSSqlAggregate.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using TileRendering; using System.IO; using System.Drawing; using System.Drawing.Imaging; [Serializable] [SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates = true, IsInvariantToNulls = true, IsInvariantToOrder = false, IsNullIfEmpty = false, MaxByteSize = -1)] public struct TileAgg : IBinarySerialize { Bitmap _bitmap; ImageFormat _format; Graphics _graphics; ImageCodecInfo _codecInfo; const int TILE_SIZE = 256; Bitmap GetInitialTile() { Bitmap DrawArea = new Bitmap(TILE_SIZE, TILE_SIZE); using (Graphics xGraph = Graphics.FromImage(DrawArea)) { xGraph.FillRectangle(Brushes.Transparent, 0, 0, TILE_SIZE, TILE_SIZE); _graphics = Graphics.FromImage(DrawArea); return DrawArea; } } #region [Aggregate artifacts] public void Init() { _codecInfo = GetEncoderInfo("image/png"); _bitmap = GetInitialTile(); DetectFormat(); } public void Accumulate(SqlBinary Value) { using (MemoryStream ms = new MemoryStream()) { ms.Write(Value.Value, 0, Value.Length); ms.Seek(0, SeekOrigin.Begin); ms.Position = 0; PasteFromStreamImageToTile( ms); } } public void Merge(TileAgg Group) { PasteGroup(Group.Terminate()); } public SqlBinary Terminate() { return GetBytes(); } #endregion [Aggregate artifacts] void PasteFromStreamImageToTile( Stream stream) { using (Bitmap iconImage = new Bitmap(stream, false)) { DetectFormat(); int width = iconImage.Width; int height = iconImage.Height; var area = new Rectangle(0, 0, width, height); CopyRegionIntoImage(iconImage,area, area); } } void CopyRegionIntoImage(Bitmap srcBitmap, Rectangle srcRegion, Rectangle destRegion) { _graphics.DrawImage(srcBitmap, destRegion, srcRegion, GraphicsUnit.Pixel); srcBitmap.Dispose(); } void PasteGroup(SqlBinary Value) { using (MemoryStream ms = new MemoryStream()) { ms.Write(Value.Value, 0, Value.Length); ms.Seek(0, SeekOrigin.Begin); ms.Position = 0; PasteTile(ms); } } void PasteTile(Stream stream) { Rectangle bounds = new Rectangle(0, 0, TILE_SIZE, TILE_SIZE); CopyRegionIntoImage(new Bitmap(stream), bounds, bounds); } byte[] GetBytes() { return _bitmap.ToByteArray(ImageFormat.Png); } #region [IBinarySerialize] public void Read(BinaryReader reader) { _bitmap = new Bitmap(new MemoryStream(reader.ReadBytes((int)reader.BaseStream.Length))); DetectFormat(); } public void Write(BinaryWriter writer) { EncoderParameters encodeParams = new EncoderParameters(1); encodeParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100); _bitmap.Save(writer.BaseStream, _codecInfo, encodeParams); } #endregion [IBinarySerialize] /// <summary> ///    /// </summary> void DetectFormat() { _format = _bitmap.GetImageFormat(); } ImageCodecInfo GetEncoderInfo(string mimeType) { //     string lookupKey = mimeType.ToLower(); ImageCodecInfo foundCodec = null; Dictionary<string, ImageCodecInfo> encoders = Encoders(); if (encoders.ContainsKey(lookupKey)) { //    foundCodec = encoders[lookupKey]; } return foundCodec; } private Dictionary<string, ImageCodecInfo> Encoders() { Dictionary<string, ImageCodecInfo> encoders = new Dictionary<string, ImageCodecInfo>(); foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders()) { encoders.Add(codec.MimeType.ToLower(), codec); } return encoders; } } 


tile.FillShapeTilesIntersection tile.Shape. @StartZoom – , @EndZoom — . tile.Shapes.fillArgb tile.Shapes.strokeArgb . : AARRGGBB ,
AA – (), RR – , GG — , BB – . : DDDDFFDD.

 CREATE PROC tile.FillShapeTilesIntersection( @StartZoom INT, @EndZoom INT) AS BEGIN DECLARE @Shape GEOMETRY DECLARE @CurrentZoom INT DECLARE @ObjectTypeID INT DECLARE @fillArgb NVARCHAR(10), @strokeArgb NVARCHAR(10) IF @ObjectTypeID IS NOT NULL BEGIN SET @CurrentZoom = @StartZoom DECLARE shape_cursor CURSOR FOR SELECT o.Shape, fillARGB, strokeARGB FROM tile.Shape o OPEN shape_cursor FETCH NEXT FROM shape_cursor INTO @Shape, @fillArgb, @strokeArgb WHILE @@FETCH_STATUS = 0 BEGIN SET @CurrentZoom = @StartZoom WHILE @CurrentZoom <= @EndZoom BEGIN INSERT INTO tile.tileOverlap (Zoom, X,Y,Data) SELECT t.Zoom, t.TileX AS X,t.TileY AS Y, tile.ShapeTile(@Shape, t.Zoom, t.TileX, t.TileY, @fillArgb, @strokeArgb ,2) AS Data FROM (SELECT * FROM tile.fn_FetchGeometryTiles(@Shape,@CurrentZoom)) t SET @CurrentZoom = @CurrentZoom + 1 END FETCH NEXT FROM shape_cursor INTO @Shape, @fillArgb, @strokeArgb END CLOSE shape_cursor; DEALLOCATE shape_cursor; DELETE tile.TileOverlap END END 



おわりに




芁玄するず、ここで説明するタむル圢成ラむブラリはプロトタむプにすぎないこずに泚意しおください。もちろん、デヌタベヌス偎でタむルを生成するのはかなり奇劙なアプロヌチであり、倚くの教育的代替手段がありたす。しかし、この蚘事を最埌たで読んだ人は、電子地図のタむルモデルの蚭蚈に぀いおの考えを持ち、SQL Serverの空間デヌタ型の䜿甚方法を知っおいたす。
github のラむブラリ

の゜ヌスコヌドラむブラリおよびバックアップデヌタベヌスの゜ヌスコヌド3.5Mb

Source: https://habr.com/ru/post/J266363/


All Articles