1 #include <mbgl/annotation/annotation_manager.hpp>
2 #include <mbgl/annotation/annotation_source.hpp>
3 #include <mbgl/annotation/annotation_tile.hpp>
4 #include <mbgl/annotation/symbol_annotation_impl.hpp>
5 #include <mbgl/annotation/line_annotation_impl.hpp>
6 #include <mbgl/annotation/fill_annotation_impl.hpp>
7 #include <mbgl/style/style.hpp>
8 #include <mbgl/style/style_impl.hpp>
9 #include <mbgl/style/layers/symbol_layer.hpp>
10 #include <mbgl/style/layers/symbol_layer_impl.hpp>
11 #include <mbgl/style/expression/dsl.hpp>
12 #include <mbgl/storage/file_source.hpp>
13 
14 #include <boost/function_output_iterator.hpp>
15 
16 namespace mbgl {
17 
18 using namespace style;
19 
20 const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
21 const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
22 const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape.";
23 
AnnotationManager(Style & style_)24 AnnotationManager::AnnotationManager(Style& style_)
25         : style(style_) {
26 };
27 
28 AnnotationManager::~AnnotationManager() = default;
29 
setStyle(Style & style_)30 void AnnotationManager::setStyle(Style& style_) {
31     style = style_;
32 }
33 
onStyleLoaded()34 void AnnotationManager::onStyleLoaded() {
35     updateStyle();
36 }
37 
addAnnotation(const Annotation & annotation)38 AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation) {
39     std::lock_guard<std::mutex> lock(mutex);
40     AnnotationID id = nextID++;
41     Annotation::visit(annotation, [&] (const auto& annotation_) {
42         this->add(id, annotation_);
43     });
44     dirty = true;
45     return id;
46 }
47 
updateAnnotation(const AnnotationID & id,const Annotation & annotation)48 bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation) {
49     std::lock_guard<std::mutex> lock(mutex);
50     Annotation::visit(annotation, [&] (const auto& annotation_) {
51         this->update(id, annotation_);
52     });
53     return dirty;
54 }
55 
removeAnnotation(const AnnotationID & id)56 void AnnotationManager::removeAnnotation(const AnnotationID& id) {
57     std::lock_guard<std::mutex> lock(mutex);
58     remove(id);
59     dirty = true;
60 }
61 
add(const AnnotationID & id,const SymbolAnnotation & annotation)62 void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation) {
63     auto impl = std::make_shared<SymbolAnnotationImpl>(id, annotation);
64     symbolTree.insert(impl);
65     symbolAnnotations.emplace(id, impl);
66 }
67 
add(const AnnotationID & id,const LineAnnotation & annotation)68 void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation) {
69     ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
70         std::make_unique<LineAnnotationImpl>(id, annotation)).first->second;
71     impl.updateStyle(*style.get().impl);
72 }
73 
add(const AnnotationID & id,const FillAnnotation & annotation)74 void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation) {
75     ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
76         std::make_unique<FillAnnotationImpl>(id, annotation)).first->second;
77     impl.updateStyle(*style.get().impl);
78 }
79 
update(const AnnotationID & id,const SymbolAnnotation & annotation)80 void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation) {
81     auto it = symbolAnnotations.find(id);
82     if (it == symbolAnnotations.end()) {
83         assert(false); // Attempt to update a non-existent symbol annotation
84         return;
85     }
86 
87     const SymbolAnnotation& existing = it->second->annotation;
88 
89     if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) {
90         dirty = true;
91 
92         remove(id);
93         add(id, annotation);
94     }
95 }
96 
update(const AnnotationID & id,const LineAnnotation & annotation)97 void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation) {
98     auto it = shapeAnnotations.find(id);
99     if (it == shapeAnnotations.end()) {
100         assert(false); // Attempt to update a non-existent shape annotation
101         return;
102     }
103 
104     shapeAnnotations.erase(it);
105     add(id, annotation);
106     dirty = true;
107 }
108 
update(const AnnotationID & id,const FillAnnotation & annotation)109 void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation) {
110     auto it = shapeAnnotations.find(id);
111     if (it == shapeAnnotations.end()) {
112         assert(false); // Attempt to update a non-existent shape annotation
113         return;
114     }
115 
116     shapeAnnotations.erase(it);
117     add(id, annotation);
118     dirty = true;
119 }
120 
remove(const AnnotationID & id)121 void AnnotationManager::remove(const AnnotationID& id) {
122     if (symbolAnnotations.find(id) != symbolAnnotations.end()) {
123         symbolTree.remove(symbolAnnotations.at(id));
124         symbolAnnotations.erase(id);
125     } else if (shapeAnnotations.find(id) != shapeAnnotations.end()) {
126         auto it = shapeAnnotations.find(id);
127         *style.get().impl->removeLayer(it->second->layerID);
128         shapeAnnotations.erase(it);
129     } else {
130         assert(false); // Should never happen
131     }
132 }
133 
getTileData(const CanonicalTileID & tileID)134 std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const CanonicalTileID& tileID) {
135     if (symbolAnnotations.empty() && shapeAnnotations.empty())
136         return nullptr;
137 
138     auto tileData = std::make_unique<AnnotationTileData>();
139 
140     auto pointLayer = tileData->addLayer(PointLayerID);
141 
142     LatLngBounds tileBounds(tileID);
143 
144     symbolTree.query(boost::geometry::index::intersects(tileBounds),
145         boost::make_function_output_iterator([&](const auto& val){
146             val->updateLayer(tileID, *pointLayer);
147         }));
148 
149     for (const auto& shape : shapeAnnotations) {
150         shape.second->updateTileData(tileID, *tileData);
151     }
152 
153     return tileData;
154 }
155 
updateStyle()156 void AnnotationManager::updateStyle() {
157     // Create annotation source, point layer, and point bucket. We do everything via Style::Impl
158     // because we don't want annotation mutations to trigger Style::Impl::styleMutated to be set.
159     if (!style.get().impl->getSource(SourceID)) {
160         style.get().impl->addSource(std::make_unique<AnnotationSource>());
161 
162         std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
163 
164         using namespace expression::dsl;
165         layer->setSourceLayer(PointLayerID);
166         layer->setIconImage(PropertyExpression<std::string>(concat(vec(literal(SourceID + "."), toString(get("sprite"))))));
167         layer->setIconAllowOverlap(true);
168         layer->setIconIgnorePlacement(true);
169 
170         style.get().impl->addLayer(std::move(layer));
171     }
172 
173     std::lock_guard<std::mutex> lock(mutex);
174 
175     for (const auto& shape : shapeAnnotations) {
176         shape.second->updateStyle(*style.get().impl);
177     }
178 
179     for (const auto& image : images) {
180         // Call addImage even for images we may have previously added, because we must support
181         // addAnnotationImage being used to update an existing image. Creating a new image is
182         // relatively cheap, as it copies only the Immutable reference. (We can't keep track
183         // of which images need to be added because we don't know if the style is the same
184         // instance as in the last updateStyle call. If it's a new style, we need to add all
185         // images.)
186         style.get().impl->addImage(std::make_unique<style::Image>(image.second));
187     }
188 }
189 
updateData()190 void AnnotationManager::updateData() {
191     std::lock_guard<std::mutex> lock(mutex);
192     if (dirty) {
193         for (auto& tile : tiles) {
194             tile->setData(getTileData(tile->id.canonical));
195         }
196         dirty = false;
197     }
198 }
199 
addTile(AnnotationTile & tile)200 void AnnotationManager::addTile(AnnotationTile& tile) {
201     std::lock_guard<std::mutex> lock(mutex);
202     tiles.insert(&tile);
203     tile.setData(getTileData(tile.id.canonical));
204 }
205 
removeTile(AnnotationTile & tile)206 void AnnotationManager::removeTile(AnnotationTile& tile) {
207     std::lock_guard<std::mutex> lock(mutex);
208     tiles.erase(&tile);
209 }
210 
211 // To ensure that annotation images do not collide with images from the style,
212 // we prefix input image IDs with "com.mapbox.annotations".
prefixedImageID(const std::string & id)213 static std::string prefixedImageID(const std::string& id) {
214     return AnnotationManager::SourceID + "." + id;
215 }
216 
addImage(std::unique_ptr<style::Image> image)217 void AnnotationManager::addImage(std::unique_ptr<style::Image> image) {
218     std::lock_guard<std::mutex> lock(mutex);
219     const std::string id = prefixedImageID(image->getID());
220     images.erase(id);
221     auto inserted = images.emplace(id, style::Image(id, image->getImage().clone(),
222                                                     image->getPixelRatio(), image->isSdf()));
223     style.get().impl->addImage(std::make_unique<style::Image>(inserted.first->second));
224 }
225 
removeImage(const std::string & id_)226 void AnnotationManager::removeImage(const std::string& id_) {
227     std::lock_guard<std::mutex> lock(mutex);
228     const std::string id = prefixedImageID(id_);
229     images.erase(id);
230     style.get().impl->removeImage(id);
231 }
232 
getTopOffsetPixelsForImage(const std::string & id_)233 double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) {
234     std::lock_guard<std::mutex> lock(mutex);
235     const std::string id = prefixedImageID(id_);
236     auto it = images.find(id);
237     return it != images.end() ? -(it->second.getImage().size.height / it->second.getPixelRatio()) / 2 : 0;
238 }
239 
240 } // namespace mbgl
241