Очень медленная прокрутка / масштабирование с помощью GMUClusterRenderer (Google Maps Clustering) на iOS - PullRequest
0 голосов
/ 11 января 2019

Я попытаюсь объяснить мою проблему и то, что я сделал до сих пор.

Введение

Я использую библиотеку iOS Utils от Google Maps для отображения около 300 маркеров на карте.

Алгоритм, используемый для кластеризации: GMUNonHierarchicalDistanceBasedAlgorithm.

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

Это позволяет нам улучшать и / или корректировать прогнозы погоды.

Но мой опыт прокрутки / масштабирования совсем не гладкий. Кстати, я тестирую его с iPhone X ...

Давайте рассмотрим суть вопроса:

Вот как я настраиваю ClusterManager

private func configureCluster(array: [Observation]) -> Void {

     let iconGenerator = GMUDefaultClusterIconGenerator()
     let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
     let renderer = GMUDefaultClusterRenderer(mapView: mapView,
                                            clusterIconGenerator: iconGenerator)
     renderer.delegate = self
     clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
                                           renderer: renderer)
     clusterManager.add(array)
     clusterManager.cluster()
     clusterManager.setDelegate(self, mapDelegate: self)
}

Вот мой Observation класс, я постарался сделать его простым:

class Observation : NSObject, GMUClusterItem {

    static var ICON_SIZE = 30

    let timestamp: Double
    let idObs: String
    let position: CLLocationCoordinate2D
    let idPicto: [Int]
    let token: String
    let comment: String
    let altitude: Double

    init(timestamp: Double, idObs: String, coordinate: CLLocationCoordinate2D, idPicto: [Int], token: String, comment: String, altitude: Double) {

        self.timestamp = timestamp
        self.idObs = idObs
        self.position = coordinate
        self.idPicto = idPicto
        self.token = token
        self.comment = comment
        self.altitude = altitude
    }
}

И, наконец, метод делегата для рендеринга:

func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {

        if let cluster = marker.userData as? GMUCluster {
            if let listObs = cluster.items as? [Observation] {
                if listObs.count > 1 {
                    let sortedObs = listObs.sorted(by: { $0.timestamp > $1.timestamp })
                    if let mostRecentObs = sortedObs.first {
                        DispatchQueue.main.async {
                            self.setIconViewForMarker(marker: marker, obs: mostRecentObs)
                        }
                    }
                } else {
                    if let obs = listObs.last {
                        DispatchQueue.main.async {
                            self.setIconViewForMarker(marker: marker, obs: obs)
                        }
                    }
                }
            }
        }
    }

Пользователи могут отправлять только одно наблюдение, но это наблюдение может быть составлено из различных погодных явлений (таких как Облака + Дождь + Ветер) или только Дождя, если они хотят.

Чтобы дифференцировать их, если это всего лишь 1 феномен, свойство marker.iconView будет установлено напрямую.

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

func setIconViewForMarker(marker: GMSMarker, obs: Observation) -> Void {

        let isYourObs = Observation.isOwnObservation(id: obs.idObs) ? true : false

        if isYourObs {
           marker.iconView = Observation.viewForPhenomenomArray(ids: obs.idPicto, isYourObs: isYourObs)
        } else {
            // Observation with more than 1 phenomenom
            if obs.idPicto.count > 1 {
                marker.iconView = Observation.viewForPhenomenomArray(ids: obs.idPicto, isYourObs: isYourObs)

                // Observation with only 1 phenomenom
            } else if obs.idPicto.count == 1 {
                if let id = obs.idPicto.last {
                    marker.iconView = Observation.setImageForPhenomenom(id: id)
                }
            }
        }
    }

И последний кусок кода, чтобы показать вам, как я создаю это пользовательское представление (я думаю, что моя проблема, вероятно, здесь)

class func viewForPhenomenomArray(ids: [Int], isYourObs: Bool) -> UIView {

        let popupView = UIView()

        popupView.frame = CGRect.init(x: 0, y: 0, width: (ICON_SIZE * ids.count) + ((ids.count + 1) * 5) , height: ICON_SIZE)

        if (isYourObs) {
            popupView.backgroundColor = UIColor(red:0.25, green:0.61, blue:0.20, alpha:1)
        } else {
           popupView.backgroundColor = UIColor(red:0.00, green:0.31, blue:0.57, alpha:1)
        }

        popupView.layer.cornerRadius = 12

        for (index, element) in ids.enumerated() {
            let imageView = UIImageView(image: Observation.getPictoFromID(id: element))
            imageView.frame = CGRect(x: ((index + 1) * 5) + index * ICON_SIZE, y: 0, width: ICON_SIZE, height: ICON_SIZE)
            popupView.addSubview(imageView)
        }

        return popupView
    }

Я также попытался с очень маленьким изображением, чтобы понять, возникает ли проблема при рендеринге большого количества PNG на карте, но, если серьезно, это iPhone X, он должен быть в состоянии отобразить на карте простой значок погоды.

Как вы думаете, я делаю что-то не так? Или это известная проблема в SDK Google Maps? (Я читал, что он установлен на 30 кадров в секунду)

Как вы думаете, рендеринг большого количества изображений (как marker.image) на карте занимает столько графического процессора? До точки, где опыт вообще не приемлем?

Если у вас есть какие-либо советы, я приму их все.

1 Ответ

0 голосов
/ 16 января 2019

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

Итак, решение, которое я нашел, это использовать buckets. Вы будете удивлены, увидев этот новый термин. Позвольте мне объяснить концепцию ведра, приведя простой пример.

предположим, что вы сохранили размеры ковшей 10, 20, 50, 100, 200, 500, 1000.

  • Теперь, если ваш кластер равен 3, он покажет 3.
  • Если размер кластера = 8, показать = 8.
  • Если размер кластера = 16, показать = 10+.
  • Если размер кластера = 22, показать = 20+.
  • Если размер кластера = 48, показать = 20+.
  • Если размер кластера = 91, показать = 50+.
  • Если размер кластера = 177, показать = 100+.
  • Если размер кластера = 502, показать = 500+.
  • Если размер кластера = 1200004, показать = 1000 +.

Теперь здесь для любого размера кластера изображения маркеров, которые будут отображаться, будут иметь значения 1, 2, 3, 4, 5, 6, 7, 8, 9, 10+, 20+, 50+, 100+, 200+, 500+, 1000+. Как он кэширует изображения, так что эти изображения будут использоваться повторно. Таким образом, время + процессор, который он использовал для создания новых изображений, уменьшается (требуется только несколько изображений).

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

Теперь вопрос в том, как этого добиться.

На самом деле, класс GMUDefaultClusterIconGenerator уже реализовал эту функциональность, вам просто нужно изменить его инициализацию на:

let iconGenerator = GMUDefaultClusterIconGenerator(buckets: [ 10, 20, 50, 100, 200, 500, 1000])

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

Дайте мне знать, если потребуется дополнительная помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...