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 | |
24 | AnnotationManager::AnnotationManager(Style& style_) |
25 | : style(style_) { |
26 | }; |
27 | |
28 | AnnotationManager::~AnnotationManager() = default; |
29 | |
30 | void AnnotationManager::setStyle(Style& style_) { |
31 | style = style_; |
32 | } |
33 | |
34 | void AnnotationManager::onStyleLoaded() { |
35 | updateStyle(); |
36 | } |
37 | |
38 | AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation) { |
39 | std::lock_guard<std::mutex> lock(mutex); |
40 | AnnotationID id = nextID++; |
41 | Annotation::visit(v: annotation, f: [&] (const auto& annotation_) { |
42 | this->add(id, annotation_); |
43 | }); |
44 | dirty = true; |
45 | return id; |
46 | } |
47 | |
48 | bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation) { |
49 | std::lock_guard<std::mutex> lock(mutex); |
50 | Annotation::visit(v: annotation, f: [&] (const auto& annotation_) { |
51 | this->update(id, annotation_); |
52 | }); |
53 | return dirty; |
54 | } |
55 | |
56 | void AnnotationManager::removeAnnotation(const AnnotationID& id) { |
57 | std::lock_guard<std::mutex> lock(mutex); |
58 | remove(id); |
59 | dirty = true; |
60 | } |
61 | |
62 | void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation) { |
63 | auto impl = std::make_shared<SymbolAnnotationImpl>(args: id, args: annotation); |
64 | symbolTree.insert(conv_or_rng: impl); |
65 | symbolAnnotations.emplace(args: id, args&: impl); |
66 | } |
67 | |
68 | void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation) { |
69 | ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(args: id, |
70 | args: std::make_unique<LineAnnotationImpl>(args: id, args: annotation)).first->second; |
71 | impl.updateStyle(*style.get().impl); |
72 | } |
73 | |
74 | void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation) { |
75 | ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(args: id, |
76 | args: std::make_unique<FillAnnotationImpl>(args: id, args: annotation)).first->second; |
77 | impl.updateStyle(*style.get().impl); |
78 | } |
79 | |
80 | void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation) { |
81 | auto it = symbolAnnotations.find(x: 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 | |
97 | void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation) { |
98 | auto it = shapeAnnotations.find(x: id); |
99 | if (it == shapeAnnotations.end()) { |
100 | assert(false); // Attempt to update a non-existent shape annotation |
101 | return; |
102 | } |
103 | |
104 | shapeAnnotations.erase(position: it); |
105 | add(id, annotation); |
106 | dirty = true; |
107 | } |
108 | |
109 | void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation) { |
110 | auto it = shapeAnnotations.find(x: id); |
111 | if (it == shapeAnnotations.end()) { |
112 | assert(false); // Attempt to update a non-existent shape annotation |
113 | return; |
114 | } |
115 | |
116 | shapeAnnotations.erase(position: it); |
117 | add(id, annotation); |
118 | dirty = true; |
119 | } |
120 | |
121 | void AnnotationManager::remove(const AnnotationID& id) { |
122 | if (symbolAnnotations.find(x: id) != symbolAnnotations.end()) { |
123 | symbolTree.remove(conv_or_rng: symbolAnnotations.at(k: id)); |
124 | symbolAnnotations.erase(x: id); |
125 | } else if (shapeAnnotations.find(x: id) != shapeAnnotations.end()) { |
126 | auto it = shapeAnnotations.find(x: id); |
127 | *style.get().impl->removeLayer(layerID: it->second->layerID); |
128 | shapeAnnotations.erase(position: it); |
129 | } else { |
130 | assert(false); // Should never happen |
131 | } |
132 | } |
133 | |
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(predicates: boost::geometry::index::intersects(g: tileBounds), |
145 | out_it: boost::make_function_output_iterator(f: [&](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 | |
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(id: SourceID)) { |
160 | style.get().impl->addSource(std::make_unique<AnnotationSource>()); |
161 | |
162 | std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(args: PointLayerID, args: SourceID); |
163 | |
164 | using namespace expression::dsl; |
165 | layer->setSourceLayer(PointLayerID); |
166 | layer->setIconImage(PropertyExpression<std::string>(concat(inputs: vec(args: literal(value: SourceID + "." ), args: toString(get(value: "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>(args: image.second)); |
187 | } |
188 | } |
189 | |
190 | void AnnotationManager::updateData() { |
191 | std::lock_guard<std::mutex> lock(mutex); |
192 | if (dirty) { |
193 | for (auto& tile : tiles) { |
194 | tile->setData(getTileData(tileID: tile->id.canonical)); |
195 | } |
196 | dirty = false; |
197 | } |
198 | } |
199 | |
200 | void AnnotationManager::addTile(AnnotationTile& tile) { |
201 | std::lock_guard<std::mutex> lock(mutex); |
202 | tiles.insert(x: &tile); |
203 | tile.setData(getTileData(tileID: tile.id.canonical)); |
204 | } |
205 | |
206 | void AnnotationManager::removeTile(AnnotationTile& tile) { |
207 | std::lock_guard<std::mutex> lock(mutex); |
208 | tiles.erase(x: &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". |
213 | static std::string prefixedImageID(const std::string& id) { |
214 | return AnnotationManager::SourceID + "." + id; |
215 | } |
216 | |
217 | void AnnotationManager::addImage(std::unique_ptr<style::Image> image) { |
218 | std::lock_guard<std::mutex> lock(mutex); |
219 | const std::string id = prefixedImageID(id: image->getID()); |
220 | images.erase(x: id); |
221 | auto inserted = images.emplace(args: id, args: style::Image(id, image->getImage().clone(), |
222 | image->getPixelRatio(), image->isSdf())); |
223 | style.get().impl->addImage(std::make_unique<style::Image>(args&: inserted.first->second)); |
224 | } |
225 | |
226 | void AnnotationManager::removeImage(const std::string& id_) { |
227 | std::lock_guard<std::mutex> lock(mutex); |
228 | const std::string id = prefixedImageID(id: id_); |
229 | images.erase(x: id); |
230 | style.get().impl->removeImage(id); |
231 | } |
232 | |
233 | double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) { |
234 | std::lock_guard<std::mutex> lock(mutex); |
235 | const std::string id = prefixedImageID(id: id_); |
236 | auto it = images.find(x: id); |
237 | return it != images.end() ? -(it->second.getImage().size.height / it->second.getPixelRatio()) / 2 : 0; |
238 | } |
239 | |
240 | } // namespace mbgl |
241 | |