mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-18 20:53:19 +00:00
Add "Business is vacant"/'disused' option to editor (#526)
Signed-off-by: map-per <map-per@gmx.de> Co-authored-by: map-per <map-per@gmx.de> Co-committed-by: map-per <map-per@gmx.de>
This commit is contained in:
@@ -153,6 +153,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
private final Map<Metadata.MetadataType, View> mDetailsBlocks = new HashMap<>();
|
||||
private final Map<Metadata.MetadataType, View> mSocialMediaBlocks = new HashMap<>();
|
||||
private MaterialButton mReset;
|
||||
private MaterialButton mDisused;
|
||||
|
||||
private EditorHostFragment mParent;
|
||||
|
||||
@@ -827,6 +828,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
osmInfo.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mReset = view.findViewById(R.id.reset);
|
||||
mReset.setOnClickListener(this);
|
||||
mDisused = view.findViewById(R.id.disused);
|
||||
mDisused.setOnClickListener(this);
|
||||
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_OPEN_HOURS, blockOpeningHours);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_PHONE_NUMBER, blockPhone);
|
||||
@@ -894,6 +897,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
mParent.addLanguage();
|
||||
else if (id == R.id.reset)
|
||||
reset();
|
||||
else if (id == R.id.disused)
|
||||
placeDisused();
|
||||
else if (id == R.id.block_outdoor_seating)
|
||||
mOutdoorSeating.toggle();
|
||||
}
|
||||
@@ -939,9 +944,12 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
if (mParent.addingNewObject())
|
||||
{
|
||||
UiUtils.hide(mReset);
|
||||
UiUtils.hide(mDisused);
|
||||
return;
|
||||
}
|
||||
|
||||
mDisused.setVisibility(Editor.nativeCanMarkPlaceAsDisused() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (Editor.nativeIsMapObjectUploaded())
|
||||
{
|
||||
mReset.setText(R.string.editor_place_doesnt_exist);
|
||||
@@ -1014,6 +1022,19 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
dialogFragment.setTextSaveListener(this::commitPlaceDoesntExists);
|
||||
}
|
||||
|
||||
private void placeDisused()
|
||||
{
|
||||
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
|
||||
.setTitle(R.string.editor_mark_business_vacant_title)
|
||||
.setMessage(R.string.editor_mark_business_vacant_description)
|
||||
.setPositiveButton(R.string.editor_submit, (dlg, which) -> {
|
||||
Editor.nativeMarkPlaceAsDisused();
|
||||
mParent.processEditedFeatures();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void commitPlaceDoesntExists(@NonNull String text)
|
||||
{
|
||||
Editor.nativePlaceDoesNotExist(text);
|
||||
|
||||
@@ -358,7 +358,7 @@ public class EditorHostFragment
|
||||
.show();
|
||||
}
|
||||
|
||||
private void processEditedFeatures()
|
||||
public void processEditedFeatures()
|
||||
{
|
||||
if (OsmOAuth.isAuthorized())
|
||||
{
|
||||
|
||||
@@ -394,7 +394,8 @@
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cv__more"
|
||||
style="@style/MwmWidget.Editor.CardView">
|
||||
style="@style/MwmWidget.Editor.CardView"
|
||||
android:layout_marginBottom="@dimen/margin_base">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -421,6 +422,17 @@
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/disused"
|
||||
style="@style/MwmWidget.M3.Button.Secondary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="@dimen/margin_quarter"
|
||||
app:backgroundTint="?cardBackground"
|
||||
android:textColor="@color/base_red"
|
||||
app:strokeColor="@color/base_red"
|
||||
android:text="@string/editor_business_vacant_button"/>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/reset"
|
||||
style="@style/MwmWidget.M3.Button.Secondary"
|
||||
|
||||
@@ -551,6 +551,14 @@
|
||||
<string name="editor_place_doesnt_exist_description">Describe what the place looks like now to send an error note to the OpenStreetMap community</string>
|
||||
<!-- Error message for "Place doesn't exist" dialog when comment is empty -->
|
||||
<string name="delete_place_empty_comment_error">Please indicate the reason for deleting the place</string>
|
||||
<!-- Button in the editor to mark business as vacant -->
|
||||
<string name="editor_business_vacant_button">Business is vacant</string>
|
||||
<!-- Title of confirmation dialog before marking business as vacant -->
|
||||
<string name="editor_mark_business_vacant_title">Mark business as vacant</string>
|
||||
<!-- Description in confirmation dialog before marking business as vacant -->
|
||||
<string name="editor_mark_business_vacant_description">Use this if the business has moved out and the space is empty and ready for a new tenant.</string>
|
||||
<!-- Submit change to OSM in the editor -->
|
||||
<string name="editor_submit">Submit</string>
|
||||
<!-- Phone number error message -->
|
||||
<string name="error_enter_correct_phone">Enter a valid phone number</string>
|
||||
<string name="error_enter_correct_web">Enter a valid web address</string>
|
||||
|
||||
@@ -277,6 +277,12 @@ JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeIsNameEd
|
||||
return g_editableMapObject.IsNameEditable();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeCanMarkPlaceAsDisused(JNIEnv * env,
|
||||
jclass clazz)
|
||||
{
|
||||
return g_editableMapObject.CanMarkPlaceAsDisused();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeIsPointType(JNIEnv * env, jclass clazz)
|
||||
{
|
||||
return g_editableMapObject.IsPointType();
|
||||
@@ -434,6 +440,11 @@ JNIEXPORT void JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeRollbackMapO
|
||||
g_framework->NativeFramework()->RollBackChanges(g_editableMapObject.GetID());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeMarkPlaceAsDisused(JNIEnv * env, jclass clazz)
|
||||
{
|
||||
g_framework->NativeFramework()->MarkPlaceAsDisused(g_editableMapObject);
|
||||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeGetAllCreatableFeatureTypes(JNIEnv * env,
|
||||
jclass clazz,
|
||||
jstring jLang)
|
||||
|
||||
@@ -99,6 +99,7 @@ public final class Editor
|
||||
|
||||
public static native boolean nativeIsAddressEditable();
|
||||
public static native boolean nativeIsNameEditable();
|
||||
public static native boolean nativeCanMarkPlaceAsDisused();
|
||||
public static native boolean nativeIsPointType();
|
||||
public static native boolean nativeIsBuilding();
|
||||
|
||||
@@ -164,6 +165,7 @@ public final class Editor
|
||||
public static native void nativeCreateNote(String text);
|
||||
public static native void nativePlaceDoesNotExist(@NonNull String comment);
|
||||
public static native void nativeRollbackMapObject();
|
||||
public static native void nativeMarkPlaceAsDisused();
|
||||
public static native void nativeCreateStandaloneNote(double lat, double lon, String text);
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ set(SRC
|
||||
xml_feature.cpp
|
||||
xml_feature.hpp
|
||||
yes_no_unknown.hpp
|
||||
keys_to_remove.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
@@ -57,6 +57,9 @@ std::string GetTypeForFeature(editor::XMLFeature const & node)
|
||||
}
|
||||
}
|
||||
|
||||
if (node.HasTag("disused:shop") || node.HasTag("disused:amenity"))
|
||||
return "vacant business";
|
||||
|
||||
if (node.HasTag("addr:housenumber") || node.HasTag("addr:street") || node.HasTag("addr:postcode"))
|
||||
return "address";
|
||||
|
||||
|
||||
135
libs/editor/keys_to_remove.hpp
Normal file
135
libs/editor/keys_to_remove.hpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
// Keys that should be removed when a place in OSM is replaced, copied from
|
||||
// https://github.com/mnalis/StreetComplete-taginfo-categorize/blob/master/sc_to_remove.txt
|
||||
|
||||
// Changes to the list: don't remove 'wheelchair' and addresses in the 'contact:' style
|
||||
|
||||
inline constexpr std::string_view kKeysToRemove[] = {
|
||||
"shop_?[1-9]?(:.*)?", "craft_?[1-9]?", "amenity_?[1-9]?", "club_?[1-9]?", "old_amenity",
|
||||
"old_shop", "information", "leisure", "office_?[1-9]?", "tourism",
|
||||
// popular shop=* / craft=* subkeys
|
||||
"marketplace", "household", "swimming_pool", "laundry", "golf", "sports", "ice_cream",
|
||||
"scooter", "music", "retail", "yes", "ticket", "newsagent", "lighting", "truck", "car_repair",
|
||||
"car_parts", "video", "fuel", "farm", "car", "tractor", "hgv", "ski", "sculptor",
|
||||
"hearing_aids", "surf", "photo", "boat", "gas", "kitchen", "anime", "builder", "hairdresser",
|
||||
"security", "bakery", "bakehouse", "fishing", "doors", "kiosk", "market", "bathroom", "lamps",
|
||||
"vacant", "insurance(:.*)?", "caravan", "gift", "bicycle", "bicycle_rental", "insulation",
|
||||
"communication", "mall", "model", "empty", "wood", "hunting", "motorcycle", "trailer",
|
||||
"camera", "water", "fireplace", "outdoor", "blacksmith", "electronics", "fan", "piercing",
|
||||
"stationery", "sensory_friendly(:.*)?", "street_vendor", "sells(:.*)?", "safety_equipment",
|
||||
// obsoleted information
|
||||
"(demolished|abandoned|disused)(:(?!bui).+)?", "was:.*", "not:.*", "damage", "created_by",
|
||||
"check_date", "opening_date", "last_checked", "checked_exists:date", "pharmacy_survey",
|
||||
"old_ref", "update", "import_uuid", "review", "fixme:atp",
|
||||
// classifications / links to external databases
|
||||
"fhrs:.*", "old_fhrs:.*", "fvst:.*", "ncat", "nat_ref", "gnis:.*", "winkelnummer",
|
||||
"type:FR:FINESS", "type:FR:APE", "kvl_hro:amenity", "ref:DK:cvr(:.*)?", "certifications?",
|
||||
"transiscope", "opendata:type", "local_ref", "official_ref",
|
||||
// names and identifications
|
||||
"name_?[1-9]?(:.*)?", ".*_name_?[1-9]?(:.*)?", "noname", "branch(:.*)?", "brand(:.*)?",
|
||||
"not:brand(:.*)?", "network(:.*)?", "operator(:.*)?", "operator_type", "ref", "ref:vatin",
|
||||
"designation", "SEP:CLAVEESC", "identifier", "ref:FR:SIRET", "ref:FR:SIREN", "ref:FR:NAF",
|
||||
"(old_)?ref:FR:prix-carburants",
|
||||
// contacts
|
||||
"contact_person", "phone(:.*)?", "phone_?[1-9]?", "emergency:phone", "emergency_telephone_code",
|
||||
"contact:(?!housenumber$|street$|place$|postcode$|city$|country$|pobox$|unit$).*",
|
||||
"mobile", "fax", "facebook", "instagram", "twitter", "youtube", "telegram", "tiktok", "email",
|
||||
"website_?[1-9]?(:.*)?", "app:.*", "ownership",
|
||||
"url", "url:official", "source_ref:url", "owner",
|
||||
// payments
|
||||
"payment(:.*)?", "payment_multi_fee", "currency(:.*)?", "cash_withdrawal(:.*)?", "fee",
|
||||
"charge", "charge_fee", "money_transfer", "donation:compensation", "paypoint",
|
||||
// generic shop/craft attributes
|
||||
"seasonal", "time", "opening_hours(:.*)?", "check_(in|out)", "wifi", "internet",
|
||||
"internet_access(:.*)?", "second_hand", "self_service", "automated", "license:.*",
|
||||
"bulk_purchase", ".*:covid19", "language:.*", "baby_feeding", "description(:.*)?",
|
||||
"description[0-9]", "min_age", "max_age", "supermarket(:.*)?", "social_facility(:.*)?",
|
||||
"functional", "trade", "wholesale", "sale", "smoking(:outside)?", "zero_waste", "origin",
|
||||
"attraction", "strapline", "dog", "showroom", "toilets?(:.*)?", "sanitary_dump_station",
|
||||
"changing_table(:.*)?", "blind", "company(:.*)?", "stroller", "walk-in",
|
||||
"webshop", "operational_status.*", "status", "drive_through", "surveillance(:.*)?",
|
||||
"outdoor_seating", "indoor_seating", "colour", "access_simple", "floor", "product_category",
|
||||
"guide", "source_url", "category", "kids_area", "kids_area:indoor", "resort", "since", "state",
|
||||
"temporary", "self_checkout", "audio_loop", "related_law(:.*)?", "official_status(:.*)?",
|
||||
// food and drink details
|
||||
"bar", "cafe", "coffee", "microroasting", "microbrewery", "brewery", "real_ale", "taproom",
|
||||
"training", "distillery", "drink(:.*)?", "cocktails", "alcohol", "wine([:_].*)?",
|
||||
"happy_hours", "diet:.*", "cuisine", "ethnic", "tasting", "breakfast", "lunch", "organic",
|
||||
"produced_on_site", "restaurant", "food", "pastry", "pastry_shop", "product", "produce",
|
||||
"chocolate", "fair_trade", "butcher", "reservation(:.*)?", "takeaway(:.*)?", "delivery(:.*)?",
|
||||
"caterer", "real_fire", "flour_fortified", "highchair", "fast_food", "pub", "snack",
|
||||
"confectionery", "drinking_water:refill",
|
||||
// related to repair shops/crafts
|
||||
"service(:.*)?", "motorcycle:.*", "repair", ".*:repair", "electronics_repair(:.*)?",
|
||||
"workshop",
|
||||
// shop=hairdresser, shop=clothes
|
||||
"unisex", "male", "female", "gender", "gender_simple", "lgbtq(:.*)?", "gay", "female:signed",
|
||||
"male:signed",
|
||||
// healthcare
|
||||
"healthcare(:.*)?", "healthcare_.*", "health", "health_.*", "speciality", "medical_.*",
|
||||
"emergency_ward", "facility(:.*)?", "activities", "healthcare_facility(:.*)?",
|
||||
"laboratory(:.*)?", "blood(:.*)?", "blood_components", "infection(:.*)?", "disease(:.*)?",
|
||||
"covid19(:.*)?", "COVID_.*", "CovidVaccineCenterId", "coronaquarantine", "hospital(:.*)?",
|
||||
"hospital_type_id", "emergency_room", "sample_collection(:.*)?", "bed_count", "capacity:beds",
|
||||
"part_time_beds", "personnel:count", "staff_count(:.*)?", "admin_staff", "doctors",
|
||||
"doctors_num", "nurses_num", "counselling_type", "testing_centres", "toilets_number",
|
||||
"urgent_care", "vaccination", "clinic", "hospital", "pharmacy", "alternative", "laboratory",
|
||||
"sample_collection", "provided_for(:.*)?", "social_facility_for", "ambulance", "ward",
|
||||
"HSE_(code|hgid|hgroup|region)", "collection_centre", "design", "AUTORIZATIE", "reg_id",
|
||||
"post_addr", "scope", "ESTADO", "NIVSOCIO", "NO", "EMP_EST", "COD_HAB", "CLA_PERS", "CLA_PRES",
|
||||
"snis_code:.*", "hfac_bed", "hfac_type", "nature", "moph_code", "IJSN:.*", "massgis:id",
|
||||
"OGD-Stmk:.*", "paho:.*", "panchayath", "pbf_contract", "pcode", "pe:minsa:.*", "who:.*",
|
||||
"pharmacy:category", "tactile_paving", "HF_(ID|TYPE|N_EN)", "RoadConn", "bin", "hiv(:.*)?",
|
||||
// accommodation & layout
|
||||
"rooms", "stars", "accommodation", "beds", "capacity(:persons)?", "laundry_service",
|
||||
"guest_house",
|
||||
// amenity=place_of_worship
|
||||
"deanery", "subject:(wikidata|wikipedia|wikimedia_commons)", "church", "church:type",
|
||||
// schools
|
||||
"capacity:(pupils|teachers)", "grades", "population:pupils(:.*)?",
|
||||
"school:(FR|gender|trust|type|type_idn|group:type)", "primary",
|
||||
// clubs
|
||||
"animal(_breeding|_training)?", "billiards(:.*)?", "board_game", "sport_1", "sport:boating",
|
||||
"boat:type", "canoe(_rental|:service)?", "kayak(_rental|:service)?",
|
||||
"sailboat(_rental|:service)?", "horse_riding", "rugby", "boules", "callsign", "card_games",
|
||||
"car_service", "catastro:ref", "chess(:.*)?", "children", "climbing(:.*)?", "club(:.*)?",
|
||||
"communication(:amateur_radio.*)", "community_centre:for", "dffr:network", "dormitory",
|
||||
"education_for:ages", "electrified", "esperanto", "events_venue", "family", "federation",
|
||||
"free_flying(:.*)?", "freemasonry(:.*)?", "free_refill", "gaelic_games(:.*)?", "membership",
|
||||
"military_service", "model_aerodrome(:.*)?", "mode_of_organisation(:.*)?", "snowmobile",
|
||||
"social_centre(:for)?", "source_dat", "tennis", "old_website", "organisation", "school_type",
|
||||
"scout(:type)?", "fraternity", "live_music", "lockable", "playground(:theme)?", "nudism",
|
||||
"music_genre", "length", "fire_station:type:FR", "cadet", "observatory:type", "tower:type",
|
||||
"zoo", "shooting", "commons", "groomer", "group_only", "hazard", "identity", "interaction",
|
||||
"logo", "maxheight", "provides", "regional", "scale", "site", "plots", "allotments",
|
||||
"local_food", "monitoring:pedestrian", "recording:automated", "yacht", "background_music",
|
||||
"url:spaceapi", "openfire", "fraternity(:.*)?",
|
||||
// misc specific attributes
|
||||
"clothes", "shoes", "tailor", "beauty", "tobacco", "carpenter", "furniture", "lottery",
|
||||
"sport", "dispensing", "tailor:.*", "gambling", "material", "raw_material", "stonemason",
|
||||
"studio", "scuba_diving(:.*)?", "polling_station", "collector", "books", "agrarian",
|
||||
"musical_instrument", "massage", "parts", "post_office(:.*)?", "religion", "denomination",
|
||||
"rental", ".*:rental", "tickets:.*", "public_transport", "goods_supply", "pet", "appliance",
|
||||
"artwork_type", "charity", "company", "crop", "dry_cleaning", "factory", "feature",
|
||||
"air_conditioning", "atm", "vending", "vending_machine", "recycling_type", "museum",
|
||||
"license_classes", "dance:.*", "isced:level", "school", "preschool", "university",
|
||||
"research_institution", "research", "member_of", "topic", "townhall:type", "parish", "police",
|
||||
"government", "thw:(lv|rb|ltg)", "office", "administration", "administrative", "association",
|
||||
"transport", "utility", "consulting", "Commercial", "commercial", "private", "taxi",
|
||||
"admin_level", "official_status", "target", "liaison", "diplomatic(:.*)?", "embassy",
|
||||
"consulate", "aeroway", "department", "faculty", "aerospace:product", "boundary", "population",
|
||||
"diocese", "depot", "cargo", "function", "game", "party", "political_party.*",
|
||||
"telecom(munication)?", "service_times", "kitchen:facilities", "it:(type|sales)",
|
||||
"cannabis:cbd", "bath:type", "bath:(open_air|sand_bath)", "animal_boarding", "animal_shelter",
|
||||
"mattress", "screen", "monitoring:weather", "public", "theatre", "culture", "library",
|
||||
"cooperative(:.*)?", "winery", "curtain", "lawyer(:.*)?", "local_authority(:.*)?", "equipment",
|
||||
"hackerspace",
|
||||
"camp_site", "camping", "bbq", "static_caravans", "emergency(:.*)?", "evacuation_cent(er|re)",
|
||||
"education", "engineering", "forestry", "foundation", "lawyer", "logistics", "military",
|
||||
"community_centre", "bank", "operational", "users_(PLWD|boy|elderly|female|girl|men)",
|
||||
"Comments?", "comments?", "entrance:(width|step_count|kerb:height)", "fenced", "motor_vehicle",
|
||||
"shelter",
|
||||
};
|
||||
@@ -668,7 +668,7 @@ void Editor::UploadChanges(string const & oauthToken, ChangesetTags tags, Finish
|
||||
{}
|
||||
|
||||
// Add tags to XMLFeature
|
||||
UpdateXMLFeatureTags(feature, journal);
|
||||
UpdateXMLFeatureTags(feature, journal, changeset);
|
||||
|
||||
// Upload XMLFeature to OSM
|
||||
LOG(LDEBUG, ("CREATE Feature (newEditor)", feature));
|
||||
@@ -686,7 +686,7 @@ void Editor::UploadChanges(string const & oauthToken, ChangesetTags tags, Finish
|
||||
XMLFeature feature = GetMatchingFeatureFromOSM(changeset, fti.m_object);
|
||||
|
||||
// Update tags of XMLFeature
|
||||
UpdateXMLFeatureTags(feature, journal);
|
||||
UpdateXMLFeatureTags(feature, journal, changeset);
|
||||
|
||||
// Upload XMLFeature to OSM
|
||||
LOG(LDEBUG, ("MODIFIED Feature (newEditor)", feature));
|
||||
@@ -1321,7 +1321,8 @@ bool Editor::IsFeatureUploadedImpl(FeaturesContainer const & features, MwmId con
|
||||
return info && info->m_uploadStatus == kUploaded;
|
||||
}
|
||||
|
||||
void Editor::UpdateXMLFeatureTags(editor::XMLFeature & feature, std::list<JournalEntry> const & journal)
|
||||
void Editor::UpdateXMLFeatureTags(editor::XMLFeature & feature, std::list<JournalEntry> const & journal,
|
||||
ChangesetWrapper & changeset)
|
||||
{
|
||||
for (JournalEntry const & entry : journal)
|
||||
{
|
||||
@@ -1335,6 +1336,13 @@ void Editor::UpdateXMLFeatureTags(editor::XMLFeature & feature, std::list<Journa
|
||||
}
|
||||
case JournalEntryType::ObjectCreated: break;
|
||||
case JournalEntryType::LegacyObject: ASSERT_FAIL(("Legacy Objects can not be edited with the new editor")); break;
|
||||
case JournalEntryType::BusinessReplacement:
|
||||
{
|
||||
BusinessReplacementData const & businessReplacementData = std::get<BusinessReplacementData>(entry.data);
|
||||
feature.OSMBusinessReplacement(businessReplacementData.old_type, businessReplacementData.new_type);
|
||||
changeset.AddChangesetTag("info:place_marked_as_disused", "yes");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "editor/changeset_wrapper.hpp"
|
||||
#include "editor/config_loader.hpp"
|
||||
#include "editor/editor_config.hpp"
|
||||
#include "editor/editor_notes.hpp"
|
||||
@@ -241,7 +242,8 @@ private:
|
||||
|
||||
static bool IsFeatureUploadedImpl(FeaturesContainer const & features, MwmId const & mwmId, uint32_t index);
|
||||
|
||||
void UpdateXMLFeatureTags(editor::XMLFeature & feature, std::list<JournalEntry> const & journal);
|
||||
static void UpdateXMLFeatureTags(editor::XMLFeature & feature, std::list<JournalEntry> const & journal,
|
||||
ChangesetWrapper & changeset);
|
||||
|
||||
/// Deleted, edited and created features.
|
||||
base::AtomicSharedPtr<FeaturesContainer> m_features;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "editor/xml_feature.hpp"
|
||||
#include "editor/keys_to_remove.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <boost/regex.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
@@ -502,6 +504,29 @@ osm::EditJournal XMLFeature::GetEditJournal() const
|
||||
entry.data = legacyObjData;
|
||||
break;
|
||||
}
|
||||
case osm::JournalEntryType::BusinessReplacement:
|
||||
{
|
||||
osm::BusinessReplacementData businessReplacementData;
|
||||
|
||||
// Old Feature Type
|
||||
std::string old_strType = getAttribute(xmlData, "old_type");
|
||||
if (old_strType.empty())
|
||||
MYTHROW(editor::InvalidJournalEntry, ("Old Feature type is empty"));
|
||||
businessReplacementData.old_type = classif().GetTypeByReadableObjectName(old_strType);
|
||||
if (businessReplacementData.old_type == IndexAndTypeMapping::INVALID_TYPE)
|
||||
MYTHROW(editor::InvalidJournalEntry, ("Invalid old Feature Type:", old_strType));
|
||||
|
||||
// New Feature Type
|
||||
std::string new_strType = getAttribute(xmlData, "new_type");
|
||||
if (new_strType.empty())
|
||||
MYTHROW(editor::InvalidJournalEntry, ("New Feature type is empty"));
|
||||
businessReplacementData.new_type = classif().GetTypeByReadableObjectName(new_strType);
|
||||
if (businessReplacementData.new_type == IndexAndTypeMapping::INVALID_TYPE)
|
||||
MYTHROW(editor::InvalidJournalEntry, ("Invalid new Feature Type:", new_strType));
|
||||
|
||||
entry.data = businessReplacementData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isHistory)
|
||||
journal.AddJournalHistoryEntry(entry);
|
||||
@@ -572,6 +597,14 @@ void XMLFeature::SetEditJournal(osm::EditJournal const & journal)
|
||||
xmlData.append_attribute("version") = legacyObjData.version.data();
|
||||
break;
|
||||
}
|
||||
case osm::JournalEntryType::BusinessReplacement:
|
||||
{
|
||||
osm::BusinessReplacementData const & businessReplacementData =
|
||||
std::get<osm::BusinessReplacementData>(entry.data);
|
||||
xmlData.append_attribute("old_type") = classif().GetReadableObjectName(businessReplacementData.old_type).data();
|
||||
xmlData.append_attribute("new_type") = classif().GetReadableObjectName(businessReplacementData.new_type).data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -675,6 +708,52 @@ void XMLFeature::UpdateOSMTag(std::string_view key, std::string_view value)
|
||||
}
|
||||
}
|
||||
|
||||
void XMLFeature::OSMBusinessReplacement(uint32_t old_type, uint32_t new_type)
|
||||
{
|
||||
std::string name = GetTagValue("name");
|
||||
|
||||
// Remove OSM tags using the list from keys_to_remove.hpp
|
||||
static boost::regex const regex([]
|
||||
{
|
||||
std::string regexPattern;
|
||||
|
||||
for (auto const & key : kKeysToRemove)
|
||||
{
|
||||
if (!regexPattern.empty())
|
||||
regexPattern.append("|");
|
||||
regexPattern.append(key);
|
||||
}
|
||||
return regexPattern;
|
||||
}());
|
||||
|
||||
ForEachTag([this](std::string_view key, std::string_view /*value*/)
|
||||
{
|
||||
if (boost::regex_match(key.begin(), key.end(), regex))
|
||||
RemoveTag(key);
|
||||
});
|
||||
|
||||
if (classif().GetReadableObjectName(new_type) == "disusedbusiness")
|
||||
{
|
||||
// Mark as 'disused'
|
||||
string const strOldType = classif().GetReadableObjectName(old_type);
|
||||
strings::SimpleTokenizer iter(strOldType, "-");
|
||||
string_view const key = *iter;
|
||||
if (++iter)
|
||||
SetTagValue("disused:" + std::string(key), *iter);
|
||||
else
|
||||
SetTagValue("disused:" + std::string(key), "yes");
|
||||
|
||||
if (!name.empty())
|
||||
SetTagValue("old_name", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new category tag
|
||||
ASSERT_FAIL("Only marking places as 'disused' is implemented yet. Wrong new_type: " +
|
||||
classif().GetReadableObjectName(new_type));
|
||||
}
|
||||
}
|
||||
|
||||
string XMLFeature::GetAttribute(string const & key) const
|
||||
{
|
||||
return GetRootNode().attribute(key.data()).value();
|
||||
|
||||
@@ -187,6 +187,8 @@ public:
|
||||
|
||||
/// Wrapper for SetTagValue and RemoveTag, avoids duplication for similar alternative osm tags
|
||||
void UpdateOSMTag(std::string_view key, std::string_view value);
|
||||
/// Replace an old business with a new business
|
||||
void OSMBusinessReplacement(uint32_t old_type, uint32_t new_type);
|
||||
|
||||
std::string GetAttribute(std::string const & key) const;
|
||||
void SetAttribute(std::string const & key, std::string const & value);
|
||||
|
||||
@@ -41,6 +41,14 @@ void EditJournal::MarkAsCreated(uint32_t type, feature::GeomType geomType, m2::P
|
||||
AddJournalEntry({JournalEntryType::ObjectCreated, time(nullptr), osm::ObjCreateData{type, geomType, mercator}});
|
||||
}
|
||||
|
||||
void EditJournal::AddBusinessReplacement(uint32_t old_type, uint32_t new_type)
|
||||
{
|
||||
LOG(LDEBUG, ("Business of type ", classif().GetReadableObjectName(old_type), " was replaced by a ",
|
||||
classif().GetReadableObjectName(new_type)));
|
||||
AddJournalEntry(
|
||||
{JournalEntryType::BusinessReplacement, time(nullptr), osm::BusinessReplacementData{old_type, new_type}});
|
||||
}
|
||||
|
||||
void EditJournal::AddJournalEntry(JournalEntry entry)
|
||||
{
|
||||
m_journal.push_back(std::move(entry));
|
||||
@@ -103,6 +111,15 @@ std::string EditJournal::ToString(osm::JournalEntry const & journalEntry)
|
||||
LegacyObjData const & legacyObjData = std::get<LegacyObjData>(journalEntry.data);
|
||||
return ToString(journalEntry.journalEntryType).append(": version=\"").append(legacyObjData.version).append("\"");
|
||||
}
|
||||
case osm::JournalEntryType::BusinessReplacement:
|
||||
{
|
||||
BusinessReplacementData const & businessReplacementData = std::get<BusinessReplacementData>(journalEntry.data);
|
||||
return ToString(journalEntry.journalEntryType)
|
||||
.append(": Category changed from ")
|
||||
.append(classif().GetReadableObjectName(businessReplacementData.old_type))
|
||||
.append(" to ")
|
||||
.append(classif().GetReadableObjectName(businessReplacementData.new_type));
|
||||
}
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@@ -114,6 +131,7 @@ std::string EditJournal::ToString(osm::JournalEntryType journalEntryType)
|
||||
case osm::JournalEntryType::TagModification: return "TagModification";
|
||||
case osm::JournalEntryType::ObjectCreated: return "ObjectCreated";
|
||||
case osm::JournalEntryType::LegacyObject: return "LegacyObject";
|
||||
case osm::JournalEntryType::BusinessReplacement: return "BusinessReplacement";
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@@ -126,6 +144,8 @@ std::optional<JournalEntryType> EditJournal::TypeFromString(std::string const &
|
||||
return JournalEntryType::ObjectCreated;
|
||||
else if (entryType == "LegacyObject")
|
||||
return JournalEntryType::LegacyObject;
|
||||
else if (entryType == "BusinessReplacement")
|
||||
return JournalEntryType::BusinessReplacement;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ enum class JournalEntryType
|
||||
TagModification,
|
||||
ObjectCreated,
|
||||
LegacyObject, // object without full journal history, used for transition to new editor
|
||||
BusinessReplacement,
|
||||
// Possible future values: ObjectDeleted, ObjectDisused, ObjectNotDisused, LocationChanged, FeatureTypeChanged
|
||||
};
|
||||
|
||||
@@ -38,11 +39,17 @@ struct LegacyObjData
|
||||
std::string version;
|
||||
};
|
||||
|
||||
struct BusinessReplacementData
|
||||
{
|
||||
uint32_t old_type;
|
||||
uint32_t new_type;
|
||||
};
|
||||
|
||||
struct JournalEntry
|
||||
{
|
||||
JournalEntryType journalEntryType = JournalEntryType::TagModification;
|
||||
time_t timestamp;
|
||||
std::variant<TagModData, ObjCreateData, LegacyObjData> data;
|
||||
std::variant<TagModData, ObjCreateData, LegacyObjData, BusinessReplacementData> data;
|
||||
};
|
||||
|
||||
/// Used to determine whether existing OSM object should be updated or new one created
|
||||
@@ -69,6 +76,9 @@ public:
|
||||
/// Log object creation in the journal
|
||||
void MarkAsCreated(uint32_t type, feature::GeomType geomType, m2::PointD mercator);
|
||||
|
||||
/// Log business replacement in the journal
|
||||
void AddBusinessReplacement(uint32_t old_type, uint32_t new_type);
|
||||
|
||||
void AddJournalEntry(JournalEntry entry);
|
||||
|
||||
/// Clear Journal and move content to journalHistory, used after upload to OSM
|
||||
|
||||
@@ -87,6 +87,32 @@ vector<MapObject::MetadataID> EditableMapObject::GetEditableProperties() const
|
||||
return props;
|
||||
}
|
||||
|
||||
bool EditableMapObject::CanMarkPlaceAsDisused() const
|
||||
{
|
||||
if (GetEditingLifecycle() == EditingLifecycle::CREATED)
|
||||
return false;
|
||||
|
||||
auto types = GetTypes();
|
||||
types.SortBySpec();
|
||||
uint32_t mainType = *types.begin();
|
||||
std::string mainTypeStr = classif().GetReadableObjectName(mainType);
|
||||
|
||||
constexpr string_view typePrefixes[] = {
|
||||
"shop",
|
||||
"amenity-restaurant",
|
||||
"amenity-fast_food",
|
||||
"amenity-cafe",
|
||||
"amenity-pub",
|
||||
"amenity-bar",
|
||||
};
|
||||
|
||||
for (auto const & typePrefix : typePrefixes)
|
||||
if (mainTypeStr.starts_with(typePrefix))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NamesDataSource EditableMapObject::GetNamesDataSource()
|
||||
{
|
||||
auto const mwmInfo = GetID().m_mwmId.GetInfo();
|
||||
@@ -656,6 +682,16 @@ void EditableMapObject::MarkAsCreated(uint32_t type, feature::GeomType geomType,
|
||||
m_journal.MarkAsCreated(type, geomType, std::move(mercator));
|
||||
}
|
||||
|
||||
void EditableMapObject::MarkAsDisused()
|
||||
{
|
||||
auto types = GetTypes();
|
||||
types.SortBySpec();
|
||||
uint32_t old_type = *types.begin();
|
||||
uint32_t new_type = classif().GetTypeByReadableObjectName("disusedbusiness");
|
||||
ApplyBusinessReplacement(new_type);
|
||||
m_journal.AddBusinessReplacement(old_type, new_type);
|
||||
}
|
||||
|
||||
void EditableMapObject::ClearJournal()
|
||||
{
|
||||
m_journal.Clear();
|
||||
@@ -673,7 +709,7 @@ void EditableMapObject::ApplyEditsFromJournal(EditJournal const & editJournal)
|
||||
void EditableMapObject::ApplyJournalEntry(JournalEntry const & entry)
|
||||
{
|
||||
LOG(LDEBUG, ("Applying Journal Entry: ", osm::EditJournal::ToString(entry)));
|
||||
// Todo
|
||||
|
||||
switch (entry.journalEntryType)
|
||||
{
|
||||
case JournalEntryType::TagModification:
|
||||
@@ -760,6 +796,12 @@ void EditableMapObject::ApplyJournalEntry(JournalEntry const & entry)
|
||||
ASSERT_FAIL(("Legacy Objects can not be loaded from Journal"));
|
||||
break;
|
||||
}
|
||||
case JournalEntryType::BusinessReplacement:
|
||||
{
|
||||
BusinessReplacementData const & businessReplacementData = std::get<BusinessReplacementData>(entry.data);
|
||||
ApplyBusinessReplacement(businessReplacementData.new_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,6 +901,47 @@ void EditableMapObject::LogDiffInJournal(EditableMapObject const & unedited_emo)
|
||||
}
|
||||
}
|
||||
|
||||
void EditableMapObject::ApplyBusinessReplacement(uint32_t new_type)
|
||||
{
|
||||
// Types
|
||||
feature::TypesHolder new_feature_types;
|
||||
|
||||
new_feature_types.Add(new_type); // Update feature type
|
||||
|
||||
std::string wheelchairType = feature::GetReadableWheelchairType(m_types);
|
||||
if (!wheelchairType.empty())
|
||||
new_feature_types.SafeAdd(classif().GetTypeByReadableObjectName(wheelchairType));
|
||||
|
||||
std::vector<uint32_t> const buildingTypes = ftypes::IsBuildingChecker::Instance().GetTypes();
|
||||
for (uint32_t const & type : buildingTypes)
|
||||
if (m_types.Has(type))
|
||||
new_feature_types.SafeAdd(type);
|
||||
|
||||
m_types = new_feature_types;
|
||||
|
||||
// Names
|
||||
m_name.Clear();
|
||||
|
||||
// Metadata
|
||||
feature::Metadata new_metadata;
|
||||
|
||||
constexpr MetadataID metadataToKeep[] = {
|
||||
MetadataID::FMD_WHEELCHAIR,
|
||||
MetadataID::FMD_POSTCODE,
|
||||
MetadataID::FMD_LEVEL,
|
||||
MetadataID::FMD_ELE,
|
||||
MetadataID::FMD_HEIGHT,
|
||||
MetadataID::FMD_MIN_HEIGHT,
|
||||
MetadataID::FMD_BUILDING_LEVELS,
|
||||
MetadataID::FMD_BUILDING_MIN_LEVEL
|
||||
};
|
||||
|
||||
for (MetadataID const & metadataID : metadataToKeep)
|
||||
new_metadata.Set(metadataID, std::string(m_metadata.Get(metadataID)));
|
||||
|
||||
m_metadata = new_metadata;
|
||||
}
|
||||
|
||||
bool AreObjectsEqualIgnoringStreet(EditableMapObject const & lhs, EditableMapObject const & rhs)
|
||||
{
|
||||
feature::TypesHolder const & lhsTypes = lhs.GetTypes();
|
||||
|
||||
@@ -78,6 +78,8 @@ public:
|
||||
/// All store/load/valid operations will be via MetadataEntryIFace interface instead of switch-case.
|
||||
std::vector<MetadataID> GetEditableProperties() const;
|
||||
|
||||
bool CanMarkPlaceAsDisused() const;
|
||||
|
||||
/// See comment for NamesDataSource class.
|
||||
NamesDataSource GetNamesDataSource();
|
||||
LocalizedStreet const & GetStreet() const;
|
||||
@@ -141,11 +143,16 @@ public:
|
||||
void SetJournal(EditJournal && editJournal);
|
||||
EditingLifecycle GetEditingLifecycle() const;
|
||||
void MarkAsCreated(uint32_t type, feature::GeomType geomType, m2::PointD mercator);
|
||||
void MarkAsDisused();
|
||||
void ClearJournal();
|
||||
void ApplyEditsFromJournal(EditJournal const & journal);
|
||||
void ApplyJournalEntry(JournalEntry const & entry);
|
||||
void LogDiffInJournal(EditableMapObject const & unedited_emo);
|
||||
|
||||
private:
|
||||
void ApplyBusinessReplacement(uint32_t new_type);
|
||||
|
||||
public:
|
||||
/// Check whether langCode can be used as default name.
|
||||
static bool CanUseAsDefaultName(int8_t const langCode, std::vector<int8_t> const & nativeMwmLanguages);
|
||||
|
||||
|
||||
@@ -3091,6 +3091,13 @@ void Framework::DeleteFeature(FeatureID const & fid)
|
||||
UpdatePlacePageInfoForCurrentSelection();
|
||||
}
|
||||
|
||||
void Framework::MarkPlaceAsDisused(osm::EditableMapObject emo)
|
||||
{
|
||||
emo.MarkAsDisused();
|
||||
osm::Editor::Instance().SaveEditedFeature(emo);
|
||||
UpdatePlacePageInfoForCurrentSelection();
|
||||
}
|
||||
|
||||
osm::NewFeatureCategories Framework::GetEditorCategories() const
|
||||
{
|
||||
return osm::Editor::Instance().GetNewFeatureCategories();
|
||||
|
||||
@@ -755,6 +755,7 @@ public:
|
||||
bool GetEditableMapObject(FeatureID const & fid, osm::EditableMapObject & emo) const;
|
||||
osm::Editor::SaveResult SaveEditedMapObject(osm::EditableMapObject emo);
|
||||
void DeleteFeature(FeatureID const & fid);
|
||||
void MarkPlaceAsDisused(osm::EditableMapObject emo);
|
||||
osm::NewFeatureCategories GetEditorCategories() const;
|
||||
bool RollBackChanges(FeatureID const & fid);
|
||||
void CreateNote(osm::MapObject const & mapObject, osm::Editor::NoteProblemType const type, std::string const & note);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
271DC2172EC60C0C00442D94 /* keys_to_remove.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 271DC2162EC60C0C00442D94 /* keys_to_remove.hpp */; };
|
||||
340C20DE1C3E4DFD00111D22 /* osm_auth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 340C20DC1C3E4DFD00111D22 /* osm_auth.cpp */; };
|
||||
340C20DF1C3E4DFD00111D22 /* osm_auth.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 340C20DD1C3E4DFD00111D22 /* osm_auth.hpp */; };
|
||||
340DC8291C4E71E500EAA2CC /* changeset_wrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 340DC8271C4E71E500EAA2CC /* changeset_wrapper.cpp */; };
|
||||
@@ -75,6 +76,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
270C9C212E16AABF00ABA688 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; name = module.modulemap; path = ../../libs/editor/module.modulemap; sourceTree = SOURCE_ROOT; };
|
||||
271DC2162EC60C0C00442D94 /* keys_to_remove.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = keys_to_remove.hpp; sourceTree = "<group>"; };
|
||||
340C20DC1C3E4DFD00111D22 /* osm_auth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = osm_auth.cpp; sourceTree = "<group>"; };
|
||||
340C20DD1C3E4DFD00111D22 /* osm_auth.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = osm_auth.hpp; sourceTree = "<group>"; };
|
||||
340DC8271C4E71E500EAA2CC /* changeset_wrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = changeset_wrapper.cpp; sourceTree = "<group>"; };
|
||||
@@ -240,6 +242,7 @@
|
||||
6715560420BEC331002BA3B4 /* edits_migration.hpp */,
|
||||
3D052486200F62ED00F24998 /* feature_matcher.cpp */,
|
||||
3D052485200F62ED00F24998 /* feature_matcher.hpp */,
|
||||
271DC2162EC60C0C00442D94 /* keys_to_remove.hpp */,
|
||||
6715565220BF0F86002BA3B4 /* new_feature_categories.cpp */,
|
||||
6715565320BF0F87002BA3B4 /* new_feature_categories.hpp */,
|
||||
341138741C15AE42002E3B3E /* opening_hours_ui.cpp */,
|
||||
@@ -337,6 +340,7 @@
|
||||
6715565520BF0F87002BA3B4 /* new_feature_categories.hpp in Headers */,
|
||||
6715560820BEC332002BA3B4 /* edits_migration.hpp in Headers */,
|
||||
3D052487200F62EE00F24998 /* feature_matcher.hpp in Headers */,
|
||||
271DC2172EC60C0C00442D94 /* keys_to_remove.hpp in Headers */,
|
||||
3411387B1C15AE42002E3B3E /* ui2oh.hpp in Headers */,
|
||||
341138791C15AE42002E3B3E /* opening_hours_ui.hpp in Headers */,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user