Построение точек так, чтобы они не перекрывались, если у них одинаковые координаты

#javascript #plot #overlap #points

#javascript #построение #перекрытие #Очки

Вопрос:

У меня есть функция, которая принимает долготу и широту и преобразует их в x и y для построения графика. Преобразование в X и Y работает нормально, и это не то, с чем у меня проблема.

Я хочу убедиться, что две точки не нанесены в одном и том же месте. В одном наборе результатов их около 30 друг над другом (поскольку они имеют одинаковые широту и долготу), это число могло бы быть намного больше.

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

Код написан на Javascript, но он очень общий, поэтому я думаю, что это немного неуместно.

Мой код выглядит следующим образом:

 var prevLong, prevLat, rand = 1, line = 1, spread = 8, i = 0;

function plot_points(long, lat){

    // CODE HERE TO CONVERT long and lat into x and y

    // System to not overlap the points 
    if((prevLat == lat) amp;amp;  (prevLong == long)) {

        if(rand==1) {
            x  = spread*line;
        } else if(rand==2) {
            x -= spread*line;
        }   else if(rand==3) {
            y  = spread*line;
        }   else if(rand==4) {
            y -= spread*line;
        }   else if(rand==5) {
            x  = spread*line;
            y  = spread*line;
        }   else if(rand==6) {
            x -= spread*line;
            y -= spread*line;
        }   else if(rand==7) {
            x  = spread*line;
            y -= spread*line;
        }   else if(rand==8) {
            x -= spread*line;
            y  = spread*line;
        // x = double
        }   else if(rand==9) {
            x  = spread*line;
            y  = spread;
        }   else if(rand==10) {
            x  = spread;
            y  = spread*line;
        }   else if(rand==11) {
            x -= spread*line;
            y -= spread;
        }   else if(rand==12) {
            x -= spread;
            y -= spread*line;
        }   else if(rand==13) {
            x  = spread*line;
            y -= spread;
        }   else if(rand==14) {
            x  = spread;
            y -= spread*line;
        }   else if(rand==15) {
            x  = spread*line;
            y -= spread;
        }   else if(rand==16) {
            x  = spread;
            y -= spread*line;
        } else if(rand==17) {
            x -= spread*line;
            y  = spread;
        }   else if(rand==18) {
            x -= spread;
            y  = spread*line;
        }   else if(rand==19) {
            x -= spread*line;
            y  = spread;
        }   else if(rand==20) {
            x -= spread;
            y  = spread*line;
        }

        if(rand == 20) {rand = 1; line  ; } else { rand  ;  }
        i  
    } else {

        line = 1;
        i = 0; 

    }

    prevLat = latitude;
    prevLong = longitude;

    return [x,y];

}
  

Это результат: вывод

Это работает некорректно, и я даже не знаю, правильно ли я вообще подхожу к проблеме.

Кому-нибудь приходилось делать это раньше? Какой метод вы бы предложили?

Комментарии:

1. Открыты ли вы для альтернативных реализаций представления плотности? Либо по диаметру, цвету, либо по какому-либо другому варианту?

2. Вы имеете в виду, например, объединение точек с одинаковыми координатами в одну точку, а затем использование размера / цвета для отображения плотности? Каждая точка представляет разные фрагменты данных, поэтому их необходимо идентифицировать отдельно. Т. е. из одной точки поступает много разных значений, которые являются уникальными.

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

Ответ №1:

Начните с группировки ваших координат. Ваше преобразование long, lat -> x, y может не потребоваться до последнего шага. Один из методов заключается в создании хэш-карты, где вы сохраняете каждую позицию long / lat и количество для каждой позиции.

Затем вы преобразуете каждую позицию long, latitude в координаты x, y и используете count, чтобы решить, как отображать точки, центрированные вокруг позиции x, y, но с размером в соответствии с количеством точек.

Рабочий алгоритм визуализации квадрата таков:

 var count = 30; // example
result = [];

var width = count/Math.sqrt(count);
width = Math.floor(width);
height = Math.floor(count/width);
var remain = count % width;

var i,j;

for(i = 0; i < width; i  )
  for(j = 0; j < height; j  )
    result.push([x-Math.floor(width/2) i, y-Math.floor(height/2) j]; 
for(i = 0; i < remain; i  )
  result.push([x-Math.floor(width/2) i, y-Math.floor(height/2) j];
  

Входные данные — количество точек, центральные координаты x, y, выходные данные — массив точек для рендеринга.

Последняя строка — это оставшиеся точки, например, квадрат с 15 точками отображает:

 OOOO
OOOO
OOOO
OOO
  

Комментарии:

1. Этот подход кажется допустимым. Я не могу протестировать это в данный момент, но я вернусь завтра с тем, что произошло. Спасибо!

Ответ №2:

Используя решение Эрнелли в качестве примера, мне удалось заставить построение работать правильно. Это кажется немного затянутым, но это работает, и это не медленно.

Решение Эрнелли содержит ошибку во втором цикле for:

 for(j = 0; i < height; i  )
  

Должно быть:

 for(j = 0; j < height; j  )
  

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

 var countArray = new Array();

// Go through every point and group same co-ordinates together 
for (var i = pointArray.length-1; i > -1; i--) {
    if(pointArray[i] != undefined) 
    { 
        var found = 0;

        // Check if this point is already in array
        for (var j = countArray.length-1; j > -1; j--)
        {
            if(countArray[j] != undefined)
            {
                if((pointArray[i].getBBox().x == countArray[j]["x"]) amp;amp; (pointArray[i].getBBox().y == countArray[j]["y"]))
                {
                    countArray[j]["points"].push(pointArray[i]);
                    found = 1;
                }
            } 
        }

        // The point is not in the array, so add it
        if(found != 1) 
        {
            var index = countArray.length;
            countArray[index] = new Array();
            countArray[index]["x"] = pointArray[i].getBBox().x;
            countArray[index]["y"] = pointArray[i].getBBox().y;
            countArray[index]["points"] = new Array();
            countArray[index]["points"].push(pointArray[i]);
        }

    }
}

// For each co-ordinate
for (var j = countArray.length-1; j > -1; j--)
{
    if(countArray[j] != undefined)
    {   
        // If there is more than one point
        if(countArray[j]["points"].length > 1) {

            // Calculate the square points
            var count = countArray[j]["points"].length;
            var x = countArray[j]["x"];
            var y = countArray[j]["x"];
            result = [];

            var width = count/Math.sqrt(count);
            width = Math.floor(width);
            height = Math.floor(count/width);
            var remain = count % width;


            var i2,j2, xx, yy;
            var space = 8;
            for(i2 = 0; i2 < width*space; i2 =space)
            {
              for(j2 = 0; j2 < height*space; j2 =space)
                {
                result.push([(Math.floor(width/2) i2), (Math.floor(height/2) j2)]); 
                }   
            }
            for(i2 = 0; i2 < remain*space; i2 =space)
            {
              result.push([(Math.floor(width/2) i2), (Math.floor(height/2) j2)]);
            }

            // Go through each point and then translate it to it's new position
            for (var jj = countArray[j]["points"].length-1; jj > -1; jj--)
            {
                if(countArray[j]["points"][jj] != undefined)
                {
                    if(result[jj] != undefined)
                    {   
                        countArray[j]["points"][jj].translate(result[jj][0]-((width*8)/2), result[jj][1]-((height*8)/2))
                    }
                }
            }
        } // End if count more than 1
    } // End if undefined
}
  

Обратите внимание, что при этом используется множество raphael.js функции (такие как getBBox и translate)

Комментарии:

1. Спасибо, что нашли ошибку, я обновил свой пример кода.