Oracle Spatial - SDO_BUFFER не работает?
У меня есть таблица с SDO_Geometries, и я запрашиваю все геометрии, чтобы найти их начальную и конечную точку, а затем вставляю эти точки в другую таблицу с именем ORAHAN. Теперь моя главная цель - для каждой точки в orahan, я должен найти, пересекается ли она с другой точкой в orahan, когда дается 2-сантиметровый буфер для точек. Поэтому я пишу некоторые pl sql, используя функции Relate и Bufer, но когда я проверял некоторые записи в Map Info, я увидел, что есть точки в пределах 1 см от себя, но нет записи в таблице пересечений под названием ORAHANCROSSES. Я неправильно использую эти функции или как?
Примечание. Я использую Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64-разрядная версия и PL/SQL Release 11.2.0.1.0 - и SDO_PACKAGE.
ORAHAN насчитывает около 400 тысяч записей.(Точки и другие столбцы.)
declare
BEGIN
for curs in (select * from ORAHAN t) loop
for curs2 in (select *
from ORAHAN t2
where SDO_RELATE(t2.geoloc,SDO_GEOM.SDO_BUFFER(curs.geoloc,0.02,0.5) ,
'mask=ANYINTERACT') = 'TRUE'
and t2.mi_prinx <> curs.mi_prinx) loop
Insert INTO ORAHANCROSSES
values
(curs.Mip, curs.Startmi, curs2.Mip, curs2.Startmi);
commit;
end loop;
end loop;
END;
И это изображение карты MapInfo, которое показывает 3 точки, которые находятся близко друг к другу примерно на 1 сантиметр. Но в orahancrosses нет записей, соответствующих этим 3.
Примечание: 0,00001000 км равен 1 см
Орахан Метаданные:
select * from user_sdo_geom_metadata where table_name = 'ORAHAN';
И Диминфо:
1 ответ
Какова система координат ваших данных? И, самое главное, какую терпимость вы установили в своих метаданных?
Некоторые другие комментарии:
1) Не используйте отношение с буферным подходом. Просто используйте подход на расстоянии.
2) Вам не нужен цикл PL/SQL для такого запроса, просто используйте простой CTAS:
create table orahancrosses as
select c1.mip mip_1, c1.startmi startmi_1, c2.mip mip_2, c2.startmi startmi_2
from orahan c1, orahan c2
where sdo_within_distance (c2.geoloc, c1.geoloc, 'distance=2 unit=cm') = 'TRUE'
and c2.mi_prinx <> c1.mi_prinx;
3) Как написано, пары точек A и B, которые находятся в пределах 2 см, будут возвращены дважды: один раз как (A,B) и еще раз как (B,A). Чтобы избежать этого (и вернуть только один из случаев), напишите запрос следующим образом:
create table orahancrosses as
select c1.mip mip_1, c1.startmi startmi_1, c2.mip mip_2, c2.startmi startmi_2
from orahan c1, orahan c2
where sdo_within_distance (c2.geoloc, c1.geoloc, 'distance=2 unit=cm') = 'TRUE'
and c1.rowid < c2.rowid;
3) Обработка количества упомянутых вами точек (400000+) должна выполняться лучше с использованием техники SDO_JOIN, например:
create table orahancrosses as
select c1.mip mip_1, c1.startmi startmi_1, c2.mip mip_2, c2.startmi startmi_2
from table (
sdo_join (
'ORAHAN','GEOLOC',
'ORAHAN','GEOLOC',
'DISTANCE=2 UNIT=CM'
)
) j,
orahan c1,
orahan c2
where j.rowid1 < j.rowid2
and c1.rowid = j.rowid1
and c2.rowid = j.rowid2;
Это, вероятно, все еще займет время для обработки - в зависимости от мощности вашего сервера базы данных. Если у вас есть лицензии на Oracle Enterprise Edition, а ваше оборудование имеет надлежащую емкость (число ядер), то параллелизм может сократить затраченное время.
4) Вы говорите, что используете Oracle 11g. Какая точная версия? Версия 11.2.0.4 - это версия терминала для 11gR2. Все, что старше, больше не поддерживается. К настоящему времени вы действительно должны быть на 12cR1 (12.1.0.2). Основное преимущество 12.1.0.2 в вашем случае - функция Vector Performance Accelerator, которая ускоряет ряд пространственных функций и операторов (только если у вас есть соответствующие лицензии Oracle Spatial - она недоступна с бесплатной функцией Oracle Locator).
======================================
Используя две точки в вашем примере. Давайте посчитаем расстояние:
select sdo_geom.sdo_distance(
sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.08336913,null),null,null),
sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.07336716,null),null,null),
0.005
) distance
from dual;
DISTANCE
----------
.01000197
1 row selected.
Обратите внимание, я не указываю SRID. Предполагая, что координаты выражены в метрах, расстояние между ними действительно немного больше 1 см.
======================================
Как вы заметили, причина, по которой ваш оригинальный синтаксис не работает, заключается в допустимом отклонении, которое вы указываете для вызова SDO_BUFFER(). Вы передаете его как 0,5 (=50 см), чтобы получить буфер с радиусом 0,02 (2 см). В результате получаемый буфер эффективно растворяется в самой точке.
Например, с допуском 0,5:
select sdo_geom.sdo_buffer(sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.08336913,null),null,null),0.02,0.5) from dual;
Производит:
SDO_GEOMETRY(2001, NULL, SDO_POINT_TYPE(521554.782, 4230983.08, NULL), NULL, NULL)
При допуске 0,005:
select sdo_geom.sdo_buffer(sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.08336913,null),null,null),0.02,0.005) from dual;
Вы получаете правильный буфер:
SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 2), SDO_ORDINATE_ARRAY(521554.782, 4230983.06, 521554.802, 4230983.08, 521554.782, 4230983.1, 521554.762, 4230983.08, 521554.782, 4230983.06))
И очень близкая точка теперь совпадает с этим буфером:
select sdo_geom.relate(
sdo_geom.sdo_buffer(sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.08336913,null),null,null),0.02,0.005),
'determine',
sdo_geometry (2001,null,sdo_point_type(521554.782174622,4230983.07336716,null),null,null),
0.005
) relation
from dual;
RELATION
-------------------------
CONTAINS
1 row selected.
======================================
Теперь тот факт, что ваши данные не имеют правильного явного SRID, означает, что использование явных единиц измерения или измерений на расстоянии не будет работать. Поскольку база данных не знает, в какой системе координат находятся ваши данные, она не знает, как определить, что две точки меньше установленного числа см или м друг от друга. Все, что вы можете сделать, это предположить, что координаты указаны в метрах.
Поэтому в приведенных выше примерах замените 'DISTANCE=2 UNIT=CM'
с 'DISTANCE=0.02'