Неточности определения маркера внутри многоугольника с использованием Google Maps и SQL Server - PullRequest
1 голос
/ 02 апреля 2020

Я нарисовал многоугольник на карте Google

new google.maps.Polygon({
    paths: [
        { lat: 52.474617867242515, lng: -1.8111903062499923 },
        { lat: 51.527748727453975, lng: -2.7340418687499923 },
        { lat: 51.41825758811742, lng: -0.40494030624999233 },
    ],
    fillColor: '#FFC107',
}).setMap(map);

Я также могу щелкнуть, чтобы разместить новые маркеры на карте, передавая координаты на сервер и затем отображая цветной маркер: зеленый, если внутри полигон, красный, если снаружи.

google.maps.event.addListener(drawingManager, 'overlaycomplete', (e) => {
    axios.post('/home/testmarker', e.overlay.getPosition()).then((response) => {
        e.overlay.setIcon('http://maps.google.com/mapfiles/ms/icons/' + response.data + '-dot.png');
    });
});

На моем сервере я проверяю, попадает ли новый маркер в мой полигон. В конечном итоге будет много проверенных многоугольников, сейчас я жестко закодировал копию многоугольника внутри хранимой процедуры

CREATE PROCEDURE [dbo].[TestMarker]
    @lat varchar(20),
    @lng varchar(20)
AS

DECLARE @g geography;  
DECLARE @h geography;  
SET @g = geography::STGeomFromText('POLYGON((-1.8111903062499923 52.474617867242515, -2.7340418687499923 51.527748727453975, -0.40494030624999233 51.41825758811742, -1.8111903062499923 52.474617867242515))', 4326);
SET @h = geography::STGeomFromText('POINT(' + @lng + ' ' + @lat + ')', 4326);

SELECT @g.STContains(@h);

По углам мои маркеры точно определены как находящиеся внутри или снаружи многоугольника.

Corner Markers

Однако стороны - это другая история. Выбор точки на полпути вдоль каждой стороны и маркеры больше не будут точно идентифицироваться как внутри или снаружи многоугольника.

Sides using Geography

Я также пытался использовать тип данных геометрии, поэтому мой SP выглядит как

...
DECLARE @g geometry;  
DECLARE @h geometry;  
SET @g = geometry::STGeomFromText('POLYGON((-1.8111903062499923 52.474617867242515, -2.7340418687499923 51.527748727453975, -0.40494030624999233 51.41825758811742, -1.8111903062499923 52.474617867242515))', 0);
SET @h = geometry::STGeomFromText('POINT(' + @lng + ' ' + @lat + ')', 0);
...

Обратите внимание, я изменил SRID на 0

Углы остаются такими же точными, как и раньше, но точность по сторонам изменилось, значительно по нижнему краю

Sides using Geometry

На первом и третьем изображениях верхний ряд маркеров предыдущие, идентифицированные с использованием «географии», нижние строки - это новые, рассчитанные с использованием «геометрии».

Кто-нибудь знает, что нужно сделать, чтобы это исправить?

У меня есть подозрение, это как-то связано с SRID с географией, и метод Google Maps используют. В моем первом примере я использовал 4326, который, кажется, широко рекомендуется. Я обнаружил, что Google также должен использовать это , хотя они также используют что-то под названием 3857, у меня, похоже, нет этого в SQL Server - по крайней мере, по умолчанию. У меня нет ранее сохраненных данных, поэтому я могу легко изменить способ хранения и сравнения данных, если это необходимо.

Ответы [ 2 ]

0 голосов
/ 03 апреля 2020

Причина - несоответствие сферической модели типа GEOGRAPHY и планарной модели Google Maps. Вы используете тип GEOGRAPHY в SQLServer, который является сферическим. Он поддерживает только SRID 4326. GEOGRAPHY также использует геодезические c ребра - линия между A и B следует по кратчайшему пути на глобусе , а не по прямой линии на карте .

Прежде всего вы должны решить, какую семантику вы действительно хотите здесь?

Если вам нужен полигон с ребрами геодезии c, вы должны оставить GEOGRAPHY, но использовать ребра геодезии c в Картах Google также. Просто добавьте geodesic: true к аргументам Polygon. Края вашего многоугольника будут выглядеть немного изогнутыми, но в основном будут соответствовать границе SQLServer. Я думаю, что все еще могут быть небольшие расхождения из-за вычисления эллипсоида и сферы в SQLServer и Google Maps.

Если вам нужен полигон с ребрами, которые выглядят прямо на карте, вам не нужен тип географии, но следует использовать GEOMETRY тип SQLServer. Это должно дать вам доступ к различным SRID и плоским краям на карте, но я не уверен, что использовать, чтобы они соответствовали Google Maps.

0 голосов
/ 02 апреля 2020

и маркеры более точно не идентифицируются как внутри или снаружи многоугольника

... слегка буферизует многоугольник?

DECLARE @g geometry = 'POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'; 
SELECT @g.STBuffer(0.1).STDifference(@g);

они также используют то, что называется 3857

declare @g geometry = geometry::STGeomFromText('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))', 3857)
select @g, @g.STSrid


declare @g geometry = 'POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))', @p geometry = 'POINT(0.9999999 2.1)';
select @g.STDistance(@p), case when @g.STDistance(@p) <= 0.000001 then 1 else 0 end as tolerancecontainsYN, @g.STIntersects(@p) as intersectsYN, @g.STContains(@p) as containsYN
...