Лично я предпочитаю работать с данными в СУБД, используя SQL. Я буду использовать Microsoft SQL Server и SQL Server Management Studio.
Создаём таблицу:
CREATE TABLE [dbo].[T1](
[IntCol] [int] IDENTITY(1,1) NOT NULL,
[XmlCol] [xml] NULL)
Загружаем данные:
INSERT INTO T1(XmlCol)
SELECT * FROM OPENROWSET(BULK 'C:\work1\doc8652442.xml', SINGLE_BLOB) AS x;
Получаем таблицу координат:
DECLARE @Xdoc xml;
SET @Xdoc = (select [XmlCol] FROM [test1].[dbo].[T1]);
WITH XMLNAMESPACES (DEFAULT 'urn://x-artefacts-rosreestr-ru/outgoing/kpt/10.0.1',
'urn://x-artefacts-rosreestr-ru/commons/complex-types/entity-spatial/5.0.1' as ns3)
SELECT Parcel.value('@CadastralNumber', 'nvarchar(50)')as data,
Ordinate.value('@X', 'nvarchar(50)') as X,Ordinate.value('@Y', 'nvarchar(50)') as Y
FROM @Xdoc.nodes('//Parcel') col(Parcel)
CROSS APPLY Parcel.nodes('//ns3:Ordinate') tab(Ordinate)
Всё! Теперь мы можем делать с данными что угодно. Например нам хочется получить геометрию участков (Parcel). Геометрия хранится в элементе EntitySpatial, он может содержать несколько контуров SpatialElement – участок может быть полигоном с дырками или даже мультиплигоном. Попробуем получит SpatialElement как LineString, для этого нам понадобится несколько функций
SpatialElement=>LineString
CREATE FUNCTION [dbo].[SpatialElementToLineString](@wXml xml)
RETURNS geometry
AS
BEGIN
DECLARE @BuildString NVARCHAR(MAX);
WITH XMLNAMESPACES ('urn://x-artefacts-rosreestr-ru/commons/complex-types/entity-spatial/5.0.1' as ns3)
SELECT @BuildString = COALESCE(@BuildString + ',', '')
+ Ordinate.value('@Y', 'NVARCHAR(50)') + ' '
+ Ordinate.value('@X', 'NVARCHAR(50)')
FROM @wXml.nodes('//ns3:Ordinate') col(Ordinate);
SET @BuildString = 'LineString(' + @BuildString + ')';
return geometry::STGeomFromText(@BuildString, 0);
END
Получение всех контуров участка:
CREATE FUNCTION [dbo].[ParcelToLineString](@Xdoc xml)
RETURNS @Tbl TABLE
(
CadastralNumber nvarchar(max),
Geom geometry
)
AS
begin
WITH XMLNAMESPACES (DEFAULT 'urn://x-artefacts-rosreestr-ru/outgoing/kpt/10.0.1',
'urn://x-artefacts-rosreestr-ru/commons/complex-types/entity-spatial/5.0.1' as ns3)
insert into @Tbl(CadastralNumber, Geom)
SELECT @Xdoc.value('/*[1]/@CadastralNumber', 'nvarchar(max)') as CadastralNumber,
[dbo].[SpatialElementToLineString](Parcel.query('.')) as geom
FROM @Xdoc.nodes('//ns3:SpatialElement') col(Parcel);
RETURN;
end
Теперь создадим таблицу для хранения геометрии:
CREATE TABLE [dbo].[CadastrTbl](
[id] [int] IDENTITY(1,1) NOT NULL,
[CadastralNumber] [nvarchar](255) NULL,
[geom] [geometry] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
И заполним её
DECLARE @Xdoc xml;
SET @Xdoc = (select [XmlCol] FROM [test1].[dbo].[T1]);
DECLARE @CURSOR CURSOR
SET @CURSOR = CURSOR SCROLL
FOR
WITH XMLNAMESPACES (DEFAULT 'urn://x-artefacts-rosreestr-ru/outgoing/kpt/10.0.1')
SELECT Parcel.query('.') FROM @Xdoc.nodes('//Parcel') col(Parcel);
DECLARE @Parcel xml;
OPEN @CURSOR
FETCH NEXT FROM @CURSOR INTO @Parcel
WHILE @@FETCH_STATUS = 0
BEGIN
insert into [test1].[dbo].[CadastrTbl]([CadastralNumber],[geom])
select * from dbo.ParcelToLineString(@Parcel);
FETCH NEXT FROM @CURSOR INTO @Parcel
END
CLOSE @CURSOR
Теперь у нас есть геометрия, которую можно открыть в ГИС (например QGIS)
В QGIS мы можем сохранить наш слой в любом удобном формате, например в kml и посмотреть данные в GE:
Теперь нам не надо ждать, когда нас спасут, и мы можем взять свою жизнь в свои руки! И всё благодаря SQL.
Комментарии (11)
prostofilya
08.08.2016 04:03Что-то я не совсем понял. То есть люди писали конвертеры напрямую в требуемые форматы, а вы создали ещё одно промежуточное решение, которое также зависимо от изменений в структуре xml?
trir
08.08.2016 04:52+2Я взял бесплатные Express версии и решил задачу — обработка данных. Эти древние форматы просто не нужны — вся работа прекрасно делается в СУБД на голом SQL'е
prostofilya
08.08.2016 05:35Если вдаваться в занудство, то .shp будет помоложе sql :). База хороша, но зачем разводить фанатизм, база это хранение большого кол-ва данных, а shp — разработка, различные преобразования.
VVPV
08.08.2016 11:55Только Ваши запросы работать не всегда корректно будут, т.к. нужно учитывать порядок обхода контура, учитывать «бублики», многоконтурные участки. Еще есть понятие подучастков и вашим запросом Вы получите координаты по ним и все будет в одной куче, отстроить ничего не получится. также Росреестр выдает координаты как точками, так и линиями и все это нужно учитывать. Задача не такая простая, как может показаться на первый взгляд. Но в целом подход правильный, просто в реальной жизни его реализация будет более сложная.
trir
08.08.2016 18:18конечно, но я хотел лишь продемонстрировать возможность и не хотел чересчур усложнять…
vittore
09.08.2016 09:32Хотелось бы добавить, что работу с xml в sql server можно несколько упростить двумя вещами:
использовать
*
вместо неймспейсов:
SELECT @Xdoc.value('/*:*[1]/@CadastralNumber', 'nvarchar(max)') as CadastralNumber, [dbo].[SpatialElementToLineString](Parcel.query('.')) as geom FROM @Xdoc.nodes('//*:SpatialElement') col(Parcel);
использовать латеральный джоин ( в sql server — это cross apply / outer apply)
select t.id, X.tag1, X.tag2 from table t cross apply ( select t.xml.value('//*:tag1') tag1, t.xml.value('//*:tag2') tag2 ) X
pavelpromin
Отличный вывод! Теперь вам не надо ждать, когда вас спасут.
А вот этим кадастровым инженерам надо, ибо большинство из них не владеет sql, у них нет лицензий на MsSql и доп. инструменты (которые еще нужно изучать)
С таким же успехом они могут написать/подправить парсер xml на своем любимом языка программирования.
trir
Нет? А если найду?
У них наверняка есть 1С и скорей всего она как раз работает на MS SQL Server
trir
КИ инженеры просят .tab'ы потому что работают в MapInfo, вот MapInfo SQL Queries — вы хотите сказать, что КИ не владеют инструментами своей работы?
Между прочим, MapInfo научилась хранить геометрию в MS SQL Server, раньше чем сама СУБД