Example #1
0
FeatureID MigrateFeatureIndex(osm::Editor::ForEachFeaturesNearByFn & forEach,
                              XMLFeature const & xml,
                              FeatureStatus const featureStatus,
                              GenerateIDFn const & generateID)
{
  switch (xml.GetType())
  {
  case XMLFeature::Type::Unknown:
    MYTHROW(MigrationError, ("Migration for XMLFeature::Type::Unknown is not possible"));
  case XMLFeature::Type::Node:
    return MigrateNodeFeatureIndex(forEach, xml, featureStatus, generateID);
  case XMLFeature::Type::Way:
  case XMLFeature::Type::Relation:
    return MigrateWayOrRelatonFeatureIndex(forEach, xml, featureStatus, generateID);
  }
  UNREACHABLE();
}
Example #2
0
void Editor::UploadChanges(string const & key, string const & secret, TChangesetTags tags,
                           TFinishUploadCallback callBack)
{
    if (m_notes->NotUploadedNotesCount())
        UploadNotes(key, secret);

    if (!HaveMapEditsToUpload())
    {
        LOG(LDEBUG, ("There are no local edits to upload."));
        return;
    }

    alohalytics::LogEvent("Editor_DataSync_started");

    // TODO(AlexZ): features access should be synchronized.
    auto const upload = [this](string key, string secret, TChangesetTags tags, TFinishUploadCallback callBack)
    {
        // This lambda was designed to start after app goes into background. But for cases when user is immediately
        // coming back to the app we work with a copy, because 'for' loops below can take a significant amount of time.
        auto features = m_features;

        int uploadedFeaturesCount = 0, errorsCount = 0;
        ChangesetWrapper changeset({key, secret}, tags);
        for (auto & id : features)
        {
            for (auto & index : id.second)
            {
                FeatureTypeInfo & fti = index.second;
                // Do not process already uploaded features or those failed permanently.
                if (!NeedsUpload(fti.m_uploadStatus))
                    continue;

                string ourDebugFeatureString;

                try
                {
                    switch (fti.m_status)
                    {
                    case FeatureStatus::Untouched:
                        CHECK(false, ("It's impossible."));
                        continue;
                    case FeatureStatus::Obsolete:
                        continue;  // Obsolete features will be deleted by OSMers.
                    case FeatureStatus::Created:
                    {
                        XMLFeature feature = fti.m_feature.ToXML(true);
                        if (!fti.m_street.empty())
                            feature.SetTagValue(kAddrStreetTag, fti.m_street);
                        ourDebugFeatureString = DebugPrint(feature);

                        ASSERT_EQUAL(feature.GetType(), XMLFeature::Type::Node,
                                     ("Linear and area features creation is not supported yet."));
                        try
                        {
                            XMLFeature osmFeature = changeset.GetMatchingNodeFeatureFromOSM(fti.m_feature.GetCenter());
                            // If we are here, it means that object already exists at the given point.
                            // To avoid nodes duplication, merge and apply changes to it instead of creating an new one.
                            XMLFeature const osmFeatureCopy = osmFeature;
                            osmFeature.ApplyPatch(feature);
                            // Check to avoid uploading duplicates into OSM.
                            if (osmFeature == osmFeatureCopy)
                            {
                                LOG(LWARNING, ("Local changes are equal to OSM, feature has not been uploaded.", osmFeatureCopy));
                                // Don't delete this local change right now for user to see it in profile.
                                // It will be automatically deleted by migration code on the next maps update.
                            }
                            else
                            {
                                LOG(LDEBUG, ("Create case: uploading patched feature", osmFeature));
                                changeset.Modify(osmFeature);
                            }
                        }
                        catch (ChangesetWrapper::OsmObjectWasDeletedException const &)
                        {
                            // Object was never created by anyone else - it's safe to create it.
                            changeset.Create(feature);
                        }
                        catch (ChangesetWrapper::EmptyFeatureException const &)
                        {
                            // There is another node nearby, but it should be safe to create a new one.
                            changeset.Create(feature);
                        }
                        catch (...)
                        {
                            // Pass network or other errors to outside exception handler.
                            throw;
                        }
                    }
                    break;

                    case FeatureStatus::Modified:
                    {
                        // Do not serialize feature's type to avoid breaking OSM data.
                        // TODO: Implement correct types matching when we support modifying existing feature types.
                        XMLFeature feature = fti.m_feature.ToXML(false);
                        if (!fti.m_street.empty())
                            feature.SetTagValue(kAddrStreetTag, fti.m_street);
                        ourDebugFeatureString = DebugPrint(feature);

                        auto const originalFeaturePtr = GetOriginalFeature(fti.m_feature.GetID());
                        if (!originalFeaturePtr)
                        {
                            LOG(LERROR, ("A feature with id", fti.m_feature.GetID(), "cannot be loaded."));
                            alohalytics::LogEvent("Editor_MissingFeature_Error");
                            RemoveFeatureFromStorageIfExists(fti.m_feature.GetID());
                            continue;
                        }

                        XMLFeature osmFeature = GetMatchingFeatureFromOSM(
                                                    changeset, *originalFeaturePtr);
                        XMLFeature const osmFeatureCopy = osmFeature;
                        osmFeature.ApplyPatch(feature);
                        // Check to avoid uploading duplicates into OSM.
                        if (osmFeature == osmFeatureCopy)
                        {
                            LOG(LWARNING, ("Local changes are equal to OSM, feature has not been uploaded.", osmFeatureCopy));
                            // Don't delete this local change right now for user to see it in profile.
                            // It will be automatically deleted by migration code on the next maps update.
                        }
                        else
                        {
                            LOG(LDEBUG, ("Uploading patched feature", osmFeature));
                            changeset.Modify(osmFeature);
                        }
                    }
                    break;

                    case FeatureStatus::Deleted:
                        auto const originalFeaturePtr = GetOriginalFeature(fti.m_feature.GetID());
                        if (!originalFeaturePtr)
                        {
                            LOG(LERROR, ("A feature with id", fti.m_feature.GetID(), "cannot be loaded."));
                            alohalytics::LogEvent("Editor_MissingFeature_Error");
                            RemoveFeatureFromStorageIfExists(fti.m_feature.GetID());
                            continue;
                        }
                        changeset.Delete(GetMatchingFeatureFromOSM(
                                             changeset, *originalFeaturePtr));
                        break;
                    }
                    fti.m_uploadStatus = kUploaded;
                    fti.m_uploadError.clear();
                    ++uploadedFeaturesCount;
                }
                catch (ChangesetWrapper::OsmObjectWasDeletedException const & ex)
                {
                    fti.m_uploadStatus = kDeletedFromOSMServer;
                    fti.m_uploadError = ex.what();
                    ++errorsCount;
                    LOG(LWARNING, (ex.what()));
                }
                catch (ChangesetWrapper::RelationFeatureAreNotSupportedException const & ex)
                {
                    fti.m_uploadStatus = kRelationsAreNotSupported;
                    fti.m_uploadError = ex.what();
                    ++errorsCount;
                    LOG(LWARNING, (ex.what()));
                }
                catch (ChangesetWrapper::EmptyFeatureException const & ex)
                {
                    fti.m_uploadStatus = kWrongMatch;
                    fti.m_uploadError = ex.what();
                    ++errorsCount;
                    LOG(LWARNING, (ex.what()));
                }
                catch (RootException const & ex)
                {
                    fti.m_uploadStatus = kNeedsRetry;
                    fti.m_uploadError = ex.what();
                    ++errorsCount;
                    LOG(LWARNING, (ex.what()));
                }
                // TODO(AlexZ): Use timestamp from the server.
                fti.m_uploadAttemptTimestamp = time(nullptr);

                if (fti.m_uploadStatus != kUploaded)
                {
                    ms::LatLon const ll = MercatorBounds::ToLatLon(feature::GetCenter(fti.m_feature));
                    alohalytics::LogEvent("Editor_DataSync_error", {{"type", fti.m_uploadStatus},
                        {"details", fti.m_uploadError}, {"our", ourDebugFeatureString},
                        {"mwm", fti.m_feature.GetID().GetMwmName()},
                        {"mwm_version", strings::to_string(fti.m_feature.GetID().GetMwmVersion())}
                    },
                    alohalytics::Location::FromLatLon(ll.lat, ll.lon));
                }
                // Call Save every time we modify each feature's information.
                SaveUploadedInformation(fti);
            }
        }

        alohalytics::LogEvent("Editor_DataSync_finished", {{"errors", strings::to_string(errorsCount)},
            {"uploaded", strings::to_string(uploadedFeaturesCount)},
            {"changeset", strings::to_string(changeset.GetChangesetId())}
        });
        if (callBack)
        {
            UploadResult result = UploadResult::NothingToUpload;
            if (uploadedFeaturesCount)
                result = UploadResult::Success;
            else if (errorsCount)
                result = UploadResult::Error;
            callBack(result);
        }
    };

    // Do not run more than one upload thread at a time.
    static auto future = async(launch::async, upload, key, secret, tags, callBack);
    auto const status = future.wait_for(milliseconds(0));
    if (status == future_status::ready)
        future = async(launch::async, upload, key, secret, tags, callBack);
}