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