Intereting Posts
Recyclerviews и SwipeRefreshLayout с использованием библиотеки поддержки 23.2.0 Android: изменение размера файла растрового изображения на масштабируемый выходной файл Должны ли мы использовать анонимные классы для OnClickListeners или внутренних именованных классов? Умножение основной матрицы в OpenCV для Android Добавить javascript в WebView Возможно ли подключить API Карт Google через обратный прокси-сервер в моем приложении? Установка PhoneGap, ошибка выполнения команды 'ant' Приоритет уклонения Android TextView или сопротивление сжатию Android – Geocoder.getFromLocationName () не работает в устройстве ICS Андроиды ObjectAnimator.ofFloat не работают должным образом Facebook Android SDK v4.0.0 Проблема с ShareDialog NullPointerException CursorLoader не обновляется после изменения данных Сбой в ListView.removeFooterView (просмотр) Android Studio TransformException: Ошибка: выполнение не выполнено для задачи ': app: transformClassesWithDexForDebug' Установить страницу по умолчанию для ViewPager в Android

Android Mapview: объединение совпадающих маркеров в новый маркер

Итак, у меня есть MapView с большим количеством маркеров, большинство из которых сосредоточены в широких кластерах в милях. При увеличении маркеров перекрываются и отображаются только в одном. То, что я хочу достичь, находится на определенном уровне масштабирования, замените перекрывающиеся маркеры маркером группы, который отображает плотность маркеров, а onClick будет увеличивать масштаб, чтобы отображать все маркеры внутри. Я знаю, что могу сделать это с помощью измерений расстояния грубой силы, но должен быть более эффективный способ. У кого-нибудь есть решение или умные алгоритмы, как я могу это достичь?

Solutions Collecting From Web of "Android Mapview: объединение совпадающих маркеров в новый маркер"

Ум … при условии, что маркеры не сгруппированы, слоистые или что-то еще: почему – прежде чем показывать их – разве вы не создаете сетку определенной плотности и просто помещаете маркеры в ячейки вашей сетки?

Если вы считаете, что несколько маркеров попадают в один и тот же бит (ячейка сетки), вы можете их сгруппировать. Если вам нужна немного более умная группировка, вы также можете проверить соседние ячейки.

Возможно, это звучит немного примитивно, но:

  • Нет n ^ 2 алгоритмов
  • Нет предположения о заказе ввода
  • Нет необходимости дополнительно обрабатывать маркеры, которые не будут отображаться

Код для сетки:

Примечание. Я пришел из мира C ++ (попал сюда через тег [algorithm]), поэтому я буду придерживаться псевдо-C ++. Я не знаю API карты. Но я был бы удивлен, если бы это не могло быть эффективно переведено на любой язык / библиотеку, которую вы используете.

Вход: – список маркеров – окно просмотра прямоугольника в мировых координатах (раздел мира, который мы сейчас просматриваем)

В простейшей форме это выглядело бы примерно так:

void draw(MarkerList mlist, View v) { //binning: list<Marker> grid[densityX][densityY]; //2D array with some configurable, fixed density foreach(Marker m in mlist) { if (m.within(v)) { int2 binIdx; binIdx.x=floor(densityX*(m.coord.xv.x1)/(v.x2-v.x1)); binIdx.y=floor(densityY*(m.coord.yv.y1)/(v.y2-v.y1)); grid[binIdx.x][binIdx.y].push(m); //just push the reference } //drawing: for (int i=0; i<densityX; ++i) for (int j=0; j<densityY; ++j) { if (grid[i][j].size()>N) { GroupMarker g; g.add(grid[i][j]); //process the list of markers belonging to this cell g.draw(); } else { foreach (Marker m in grid[i][j]) m.draw() } } } 

Проблема, которая может возникнуть, заключается в том, что нежелательная разбивка сетки может появляться внутри некоторой кластерной группы, образуя два GroupMarkers. Чтобы противостоять этому, вы можете рассмотреть не только одну ячейку сетки, но также ее соседи в разделе «\ drawing», и – если сгруппированы – отметьте соседние ячейки как посещенные.

Следующее прагматичное решение, основанное на расстоянии пикселя, действительно работало лучше всего для меня:

http://www.appelsiini.net/2008/11/introduction-to-marker-clustering-with-google-maps

Я переписал ответ Cygnus X1 на Java. Поместите этот метод в свой пользовательский Overlay и измените drawSingle () и drawGroup () в соответствии с вашими потребностями. Вы также повышаете производительность, например, преобразовываете ArrayLists в примитивные массивы.

  @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { // binning: int densityX = 10; int densityY = 10; // 2D array with some configurable, fixed density List<List<List<OverlayItem>>> grid = new ArrayList<List<List<OverlayItem>>>( densityX); for(int i = 0; i<densityX; i++){ ArrayList<List<OverlayItem>> column = new ArrayList<List<OverlayItem>>(densityY); for(int j = 0; j < densityY; j++){ column.add(new ArrayList<OverlayItem>()); } grid.add(column); } for (OverlayItem m : mOverlays) { int binX; int binY; Projection proj = mapView.getProjection(); Point p = proj.toPixels(m.getPoint(), null); if (isWithin(p, mapView)) { double fractionX = ((double)px / (double)mapView.getWidth()); binX = (int) (Math.floor(densityX * fractionX)); double fractionY = ((double)py / (double)mapView.getHeight()); binY = (int) (Math .floor(densityX * fractionY)); // Log.w("PointClusterer absolute", p.x+ ", "+py); // Log.w("PointClusterer relative", fractionX+ ", "+fractionY); // Log.w("PointClusterer portion", "Marker is in portion: " + binX // + ", " + binY); grid.get(binX).get(binY).add(m); // just push the reference } } // drawing: for (int i = 0; i < densityX; i++) { for (int j = 0; j < densityY; j++) { List<OverlayItem> markerList = grid.get(i).get(j); if (markerList.size() > 1) { drawGroup(canvas, mapView, markerList); } else { // draw single marker drawSingle(canvas, mapView, markerList); } } } } private void drawGroup(Canvas canvas, MapView mapView, List<OverlayItem> markerList) { GeoPoint point = markerList.get(0).getPoint(); Point ptScreenCoord = new Point(); mapView.getProjection().toPixels(point, ptScreenCoord); Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(30); paint.setAntiAlias(true); paint.setARGB(150, 0, 0, 0); // show text to the right of the icon canvas.drawText("GROUP", ptScreenCoord.x, ptScreenCoord.y + 30, paint); } private void drawSingle(Canvas canvas, MapView mapView, List<OverlayItem> markerList) { for (OverlayItem item : markerList) { GeoPoint point = item.getPoint(); Point ptScreenCoord = new Point(); mapView.getProjection().toPixels(point, ptScreenCoord); Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(30); paint.setAntiAlias(true); paint.setARGB(150, 0, 0, 0); // show text to the right of the icon canvas.drawText("SINGLE", ptScreenCoord.x, ptScreenCoord.y + 30, paint); } } public static boolean isWithin(Point p, MapView mapView) { return (px > 0 & px < mapView.getWidth() & py > 0 & py < mapView .getHeight()); } } 

Предполагая, что ваши маркеры сгруппированы в ItemizedOverlay, вы можете создать метод, который был вызван, когда карта была увеличена. Это будет сравнивать пиксельные координаты каждого маркера, чтобы увидеть, перекрываются ли они и устанавливают флаг. Затем в методе рисования вы можете нарисовать либо сгруппированный маркер, либо индивидуумы;

Что-то вроде:

  //this would need to be wired to be called when the mapview is zoomed //it sets the drawgrouped flag if co-ordinates are close together Boolean drawGrouped=false; public void onMapZoom(MapView mapView){ //loop thru overlay items Integer i,l=this.size(); OverlayItem item; Integer deltaX=null,deltaY=null; Projection proj = mapView.getProjection(); Point p=new Point(); Integer x=null,y=null; Integer tolerance = 10; //if co-ordinates less than this draw grouped icon for(i=0;i<l;i++){ //get the item item=this.getItem(i); //convert the overlays position to pixels proj.toPixels(item.getPoint(), p); proj.toPixels(item.getPoint(), p); //compare co-ordinates if(i==0){ x=px; y=py; continue; } deltaX=Math.abs(px-x); deltaY=Math.abs(py-y); //if the co-ordinates are too far apart dont draw grouped if(deltaX>tolerance || deltaY>tolerance){ drawGrouped=false; return; } x=px; y=py; } //all co-ords are within the tolerance drawGrouped=true; } public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow){ if(drawGrouped==true){ //draw the grouped icon *needs to be optimised to only do it once drawGrouped(canvas,mapView,shadow); return; } //not grouped do regular drawing super.draw(canvas, mapView, shadow); } 

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

Основная идея состоит в том, чтобы разделить карту на квадраты на основе текущего уровня масштабирования (вы можете кэшировать вычисления на основе уровня масштабирования, чтобы избежать перерасчета при запуске пользователя) и сгруппировать их на основе того квадрата, к которому они принадлежат. Таким образом, у вас есть какая-то группировка на основе уровня масштабирования, то есть для уровня 1-5 просто нарисуйте маркеры, для уровня 5-8 группируйте их по квадратам в 20 миль, для 9-10 на площадях 50 миль и так далее на.

Вот еще один актуальный вопрос о SO, который вы можете захотеть взглянуть, но не уверен в производительности этого: Android Clustering

Если ваши маркеры сгруппированы, у вас будет справедливая идея, на каком уровне масштабирования вы должны отображать отдельные маркеры или маркер группы, например, уровень масштабирования> 17, а затем отображать отдельные маркеры, в противном случае отображать маркер группы. Я использовал код что-то вроде этого в моем ItemizedOverlay для изменения моих маркеров:

 @Override public void draw(Canvas canvas, MapView mapv, boolean shadow) { int zoom = mapv.getZoomLevel(); switch(zoom) { case 19: setMarkersForZoomLevel19(); break; case 18: setMarkersForZoomLevel18(); break; case 17: setMarkersForZoomLevel17(); break; case 16: setMarkersForZoomLevel16(); break; default: // Hide the markers or remove the overlay from the map view. mapv.getOverlays().clear(); } area.drawArea(canvas, mapv); // Putting this call here rather than at the beginning, ensures that // the Overlay items are drawn over the top of canvas stuff eg route lines. super.draw(canvas, mapv, false); } private void setMarkersForZoomLevel19() { for (JourneyOverlayItem item : mOverlays) { item.setMarker(areaPointIcon48); } } 

Если у вас есть возможность иметь отдельные маркеры в коллекции, вы можете легко получить самую большую и наименьшую широту и долготу, а разница между ними даст вам широту и долготу (это можно затем использовать для увеличения масштаба, чтобы показать Группа маркеров). Разделите промежутки на 2, и вы должны иметь центральную точку для размещения маркера группы.

Это тот подход, который я использовал. Однако это O (n ^ 2).

Штыри должны быть отсортированы на основе выдающихся.

Выбирайте штырь с наивысшим видом. Посмотрите на все контакты вокруг него. Поглотите штифты возле этого штифта.

Затем перейдите к следующему самому высокому выступу. Делать то же самое. Повторение.

Просто.

Все усложняется, если вы перемещаете карту вокруг, увеличиваете масштаб, увеличиваете масштаб и хотите, чтобы новые контакты не перерисовывались. Таким образом, вы проверяете каждый кластер, если они должны разделиться во время масштабирования, а затем вы проверяете каждый кластер, если они должны объединиться во время уменьшения масштаба. Затем вы удаляете штыри, которые исчезли, и добавьте новые контакты. Для каждого добавляемого булавки вы проверяете, должны ли они присоединиться к кластеру или сформировать собственный кластер.