void Editor::LoadMapEdits() { if (!m_delegate) { LOG(LERROR, ("Can't load any map edits, delegate has not been set.")); return; } xml_document doc; if (!m_storage->Load(doc)) return; array<pair<FeatureStatus, char const *>, 4> const sections = {{ {FeatureStatus::Deleted, kDeleteSection}, {FeatureStatus::Modified, kModifySection}, {FeatureStatus::Obsolete, kObsoleteSection}, {FeatureStatus::Created, kCreateSection} } }; int deleted = 0, obsolete = 0, modified = 0, created = 0; bool needRewriteEdits = false; // TODO(mgsergio): synchronize access to m_features. m_features.clear(); for (xml_node mwm : doc.child(kXmlRootNode).children(kXmlMwmNode)) { string const mapName = mwm.attribute("name").as_string(""); int64_t const mapVersion = mwm.attribute("version").as_llong(0); MwmSet::MwmId const mwmId = GetMwmIdByMapName(mapName); // TODO(mgsergio, AlexZ): Is it normal to have isMwmIdAlive and mapVersion // NOT equal to mwmId.GetInfo()->GetVersion() at the same time? auto const needMigrateEdits = !mwmId.IsAlive() || mapVersion != mwmId.GetInfo()->GetVersion(); needRewriteEdits |= needMigrateEdits; for (auto const & section : sections) { for (auto const nodeOrWay : mwm.child(section.second).select_nodes("node|way")) { try { XMLFeature const xml(nodeOrWay.node()); // TODO(mgsergio): A map could be renamed, we'll treat it as deleted. // The right thing to do is to try to migrate all changes anyway. if (!mwmId.IsAlive()) { LOG(LINFO, ("Mwm", mapName, "was deleted")); goto SECTION_END; } TForEachFeaturesNearByFn forEach = [this](TFeatureTypeFn && fn, m2::PointD const & point) { return ForEachFeatureAtPoint(move(fn), point); }; // TODO(mgsergio): Deleted features are not properly handled yet. auto const fid = needMigrateEdits ? editor::MigrateFeatureIndex( forEach, xml, section.first, [this, &mwmId] { return GenerateNewFeatureId(mwmId); }) : FeatureID(mwmId, xml.GetMWMFeatureIndex()); // Remove obsolete changes during migration. if (needMigrateEdits && IsObsolete(xml, fid)) continue; FeatureTypeInfo fti; if (section.first == FeatureStatus::Created) { fti.m_feature.FromXML(xml); } else { auto const originalFeaturePtr = GetOriginalFeature(fid); if (!originalFeaturePtr) { LOG(LERROR, ("A feature with id", fid, "cannot be loaded.")); alohalytics::LogEvent("Editor_MissingFeature_Error"); goto SECTION_END; } fti.m_feature = *originalFeaturePtr; fti.m_feature.ApplyPatch(xml); } fti.m_feature.SetID(fid); fti.m_street = xml.GetTagValue(kAddrStreetTag); fti.m_modificationTimestamp = xml.GetModificationTime(); ASSERT_NOT_EQUAL(my::INVALID_TIME_STAMP, fti.m_modificationTimestamp, ()); fti.m_uploadAttemptTimestamp = xml.GetUploadTime(); fti.m_uploadStatus = xml.GetUploadStatus(); fti.m_uploadError = xml.GetUploadError(); fti.m_status = section.first; switch (section.first) { case FeatureStatus::Deleted: ++deleted; break; case FeatureStatus::Modified: ++modified; break; case FeatureStatus::Obsolete: ++obsolete; break; case FeatureStatus::Created: ++created; break; case FeatureStatus::Untouched: ASSERT(false, ()); continue; } // Insert initialized structure at the end: exceptions are possible in above code. m_features[fid.m_mwmId].emplace(fid.m_index, move(fti)); } catch (editor::XMLFeatureError const & ex) { ostringstream s; nodeOrWay.node().print(s, " "); LOG(LERROR, (ex.what(), "Can't create XMLFeature in section", section.second, s.str())); } catch (editor::MigrationError const & ex) { LOG(LWARNING, (ex.Msg(), "mwmId =", mwmId, XMLFeature(nodeOrWay.node()))); } } // for nodes } // for sections SECTION_END: ; } // for mwms // Save edits with new indexes and mwm version to avoid another migration on next startup. if (needRewriteEdits) Save(); LOG(LINFO, ("Loaded", modified, "modified,", created, "created,", deleted, "deleted and", obsolete, "obsolete features.")); }
void Editor::LoadMapEdits() { if (!m_mwmIdByMapNameFn) { LOG(LERROR, ("Can't load any map edits, MwmIdByNameAndVersionFn has not been set.")); return; } xml_document doc; { string const fullFilePath = GetEditorFilePath(); xml_parse_result const res = doc.load_file(fullFilePath.c_str()); // Note: status_file_not_found is ok if user has never made any edits. if (res != status_ok && res != status_file_not_found) { LOG(LERROR, ("Can't load map edits from disk:", fullFilePath)); return; } } array<pair<FeatureStatus, char const *>, 3> const sections = {{ {FeatureStatus::Deleted, kDeleteSection}, {FeatureStatus::Modified, kModifySection}, {FeatureStatus::Created, kCreateSection} }}; int deleted = 0, modified = 0, created = 0; bool needRewriteEdits = false; for (xml_node mwm : doc.child(kXmlRootNode).children(kXmlMwmNode)) { string const mapName = mwm.attribute("name").as_string(""); int64_t const mapVersion = mwm.attribute("version").as_llong(0); MwmSet::MwmId const mwmId = m_mwmIdByMapNameFn(mapName); // TODO(mgsergio, AlexZ): Is it normal to have isMwmIdAlive and mapVersion // NOT equal to mwmId.GetInfo()->GetVersion() at the same time? auto const needMigrateEdits = !mwmId.IsAlive() || mapVersion != mwmId.GetInfo()->GetVersion(); needRewriteEdits |= needMigrateEdits; for (auto const & section : sections) { for (auto const nodeOrWay : mwm.child(section.second).select_nodes("node|way")) { try { XMLFeature const xml(nodeOrWay.node()); auto const fid = needMigrateEdits ? editor::MigrateFeatureIndex(m_forEachFeatureAtPointFn, xml) : FeatureID(mwmId, xml.GetMWMFeatureIndex()); // Remove obsolete edit during migration. if (needMigrateEdits && IsObsolete(xml, fid)) continue; FeatureTypeInfo fti; if (section.first == FeatureStatus::Created) { fti.m_feature.FromXML(xml); } else { fti.m_feature = *m_getOriginalFeatureFn(fid); fti.m_feature.ApplyPatch(xml); } fti.m_feature.SetID(fid); fti.m_street = xml.GetTagValue(kAddrStreetTag); fti.m_modificationTimestamp = xml.GetModificationTime(); ASSERT_NOT_EQUAL(my::INVALID_TIME_STAMP, fti.m_modificationTimestamp, ()); fti.m_uploadAttemptTimestamp = xml.GetUploadTime(); fti.m_uploadStatus = xml.GetUploadStatus(); fti.m_uploadError = xml.GetUploadError(); fti.m_status = section.first; switch (section.first) { case FeatureStatus::Deleted: ++deleted; break; case FeatureStatus::Modified: ++modified; break; case FeatureStatus::Created: ++created; break; case FeatureStatus::Untouched: ASSERT(false, ()); continue; } // Insert initialized structure at the end: exceptions are possible in above code. m_features[fid.m_mwmId].emplace(fid.m_index, move(fti)); } catch (editor::XMLFeatureError const & ex) { ostringstream s; nodeOrWay.node().print(s, " "); LOG(LERROR, (ex.what(), "Can't create XMLFeature in section", section.second, s.str())); } catch (editor::MigrationError const & ex) { LOG(LWARNING, (ex.Msg(), "mwmId =", mwmId, XMLFeature(nodeOrWay.node()))); } } // for nodes } // for sections } // for mwms // Save edits with new indexes and mwm version to avoid another migration on next startup. if (needRewriteEdits) Save(GetEditorFilePath()); LOG(LINFO, ("Loaded", modified, "modified,", created, "created and", deleted, "deleted features.")); }