diff --git a/OsmAnd.xcodeproj/project.pbxproj b/OsmAnd.xcodeproj/project.pbxproj index dbfb642a0a..e4a47147e6 100644 --- a/OsmAnd.xcodeproj/project.pbxproj +++ b/OsmAnd.xcodeproj/project.pbxproj @@ -450,6 +450,7 @@ 32A7EE16261BBF6C00F7A697 /* bg_point_octagon_contour@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32A7EE14261BBF6C00F7A697 /* bg_point_octagon_contour@2x.png */; }; 32A7EE1B261BBF7E00F7A697 /* bg_point_square_contour@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32A7EE19261BBF7D00F7A697 /* bg_point_square_contour@2x.png */; }; 32A7EE1C261BBF7E00F7A697 /* bg_point_square_contour@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32A7EE1A261BBF7E00F7A697 /* bg_point_square_contour@3x.png */; }; + 32AB31242E66EF6A00FAC47E /* PlaceDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32AB31232E66EF5700FAC47E /* PlaceDetailsViewController.swift */; }; 32AB48692C9C50CB005EF1D4 /* DownloadingListHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32AB48682C9C50CB005EF1D4 /* DownloadingListHelper.swift */; }; 32AB486B2C9C84B3005EF1D4 /* DownloadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32AB486A2C9C84B3005EF1D4 /* DownloadingListViewController.swift */; }; 32ABB64E2AEBC02D00491E99 /* OAWebImagesCacheHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32ABB64D2AEBC02D00491E99 /* OAWebImagesCacheHelper.mm */; }; @@ -3926,6 +3927,7 @@ 32A7EE14261BBF6C00F7A697 /* bg_point_octagon_contour@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "bg_point_octagon_contour@2x.png"; path = "Resources/Icons/bg_point_octagon_contour@2x.png"; sourceTree = ""; }; 32A7EE19261BBF7D00F7A697 /* bg_point_square_contour@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "bg_point_square_contour@2x.png"; path = "Resources/Icons/bg_point_square_contour@2x.png"; sourceTree = ""; }; 32A7EE1A261BBF7E00F7A697 /* bg_point_square_contour@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "bg_point_square_contour@3x.png"; path = "Resources/Icons/bg_point_square_contour@3x.png"; sourceTree = ""; }; + 32AB31232E66EF5700FAC47E /* PlaceDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceDetailsViewController.swift; sourceTree = ""; }; 32AB48682C9C50CB005EF1D4 /* DownloadingListHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadingListHelper.swift; sourceTree = ""; }; 32AB486A2C9C84B3005EF1D4 /* DownloadingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadingListViewController.swift; sourceTree = ""; }; 32ABB64C2AEBC02100491E99 /* OAWebImagesCacheHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAWebImagesCacheHelper.h; sourceTree = ""; }; @@ -12471,6 +12473,7 @@ DA5A7E5226C563A400F274C7 /* OAPOIViewController.h */, DA5A7E5326C563A400F274C7 /* OAPOIViewController.mm */, 3226C1A62D3E66DB008F9905 /* RenderedObjectViewController.swift */, + 32AB31232E66EF5700FAC47E /* PlaceDetailsViewController.swift */, ); path = POI; sourceTree = ""; @@ -17777,6 +17780,7 @@ 320F71372A86438B0071C0E7 /* OAGPXDocumentAdapter.mm in Sources */, 46C8412F2C32F26000E284B0 /* OAShowHideCloudAction.m in Sources */, DA5A815026C563A700F274C7 /* OACarPlayAddressSearchController.mm in Sources */, + 32AB31242E66EF6A00FAC47E /* PlaceDetailsViewController.swift in Sources */, DAA612E527E9E934003317BD /* OAPrepareBackupResult.m in Sources */, DADBE9F22A32085800C0C102 /* CompassVisibilityViewController.swift in Sources */, 46C841312C32F27500E284B0 /* OAShowHideWindAction.m in Sources */, diff --git a/Sources/Constants/OAIndexConstants.h b/Sources/Constants/OAIndexConstants.h index 8e18cf657a..0e2bdae8e5 100644 --- a/Sources/Constants/OAIndexConstants.h +++ b/Sources/Constants/OAIndexConstants.h @@ -118,4 +118,6 @@ static NSString * const MODEL_NAME_PREFIX = @"model_"; static NSString * const ONLINE_TILES_DIR = @"OsmAnd (online tiles)"; +static NSString * const XML_COLON = @"_-_"; + #endif /* OAIndexConstants_h */ diff --git a/Sources/Controllers/Map/Helpers/BaseDetailsObject.swift b/Sources/Controllers/Map/Helpers/BaseDetailsObject.swift index 398669ada7..6378562c2a 100644 --- a/Sources/Controllers/Map/Helpers/BaseDetailsObject.swift +++ b/Sources/Controllers/Map/Helpers/BaseDetailsObject.swift @@ -23,7 +23,7 @@ private enum ObjectCompleteness: UInt { @objcMembers final class BaseDetailsObject: NSObject { - var osmIds: Set + var osmIds: Set var wikidataIds: Set var objects: Array var lang: String @@ -35,7 +35,7 @@ final class BaseDetailsObject: NSObject { override init() { self.lang = "en" - self.osmIds = Set() + self.osmIds = Set() self.wikidataIds = Set() self.objects = Array() self.syntheticAmenity = OAPOI() @@ -97,8 +97,8 @@ final class BaseDetailsObject: NSObject { let osmId = getOsmId(object) let wikidata = getWikidata(object) - if osmId != -1 { - osmIds.insert(Int(osmId)) + if osmId > 0 { + osmIds.insert(UInt64(osmId)) } if let wikidata, !wikidata.isEmpty { wikidataIds.insert(wikidata) @@ -120,20 +120,20 @@ final class BaseDetailsObject: NSObject { return nil } - private func getOsmId(_ object: Any) -> Int64 { + private func getOsmId(_ object: Any) -> UInt64 { if let amenity = object as? OAPOI { return amenity.getOsmId() } else if let mapObject = object as? OAMapObject { return ObfConstants.getOsmObjectId(mapObject) } - return -1 + return 0 } func overlapsWith(_ object: Any) -> Bool { let osmId = getOsmId(object) let wikidata = getWikidata(object) - let osmIdEqual = osmId != -1 && osmIds.contains(Int(osmId)) + let osmIdEqual = osmId > 0 && osmIds.contains(UInt64(osmId)) var wikidataEqual = false if let wikidata, !wikidata.isEmpty, wikidataIds.contains(wikidata) { @@ -230,7 +230,7 @@ final class BaseDetailsObject: NSObject { guard let transportStopPoi = transportStop.poi else { return } let osmId = ObfConstants.getOsmObjectId(transportStopPoi) - osmIds.insert(Int(osmId)) + osmIds.insert(UInt64(osmId)) if let amenity = transportStop.poi { if let wikidata = amenity.getWikidata() { @@ -242,7 +242,7 @@ final class BaseDetailsObject: NSObject { private func mergeRenderedObject(_ renderedObject: OARenderedObject) { let osmId = ObfConstants.getOsmObjectId(renderedObject) - osmIds.insert(Int(osmId)) + osmIds.insert(UInt64(osmId)) if let wikidata = renderedObject.tags[WIKIDATA_TAG] as? String { wikidataIds.insert(wikidata) @@ -258,50 +258,15 @@ final class BaseDetailsObject: NSObject { var contentLocales = Set() for object in objects { - if let amenity = object as? OAPOI { - processAmenity(amenity, contentLocales: &contentLocales) - } else if let transportStop = object as? OATransportStop { - if let poi = transportStop.poi { - processAmenity(poi, contentLocales: &contentLocales) - } else { - processId(transportStop) - syntheticAmenity.copyNames(transportStop) - if syntheticAmenity.getLocation() == nil { - syntheticAmenity.latitude = transportStop.latitude - syntheticAmenity.longitude = transportStop.longitude - } - } - } else if let renderedObject = object as? OARenderedObject { - let type = ObfConstants.getOsmEntityType(renderedObject) - if let type { - let osmId = ObfConstants.getOsmObjectId(renderedObject) - let objectId = ObfConstants.createMapObjectIdFromOsmId(osmId, type: type) - - if syntheticAmenity.obfId >= 0 && objectId > 0 { - syntheticAmenity.obfId = objectId - } - } - - if syntheticAmenity.type == nil { - syntheticAmenity.copyAdditionalInfo(withMap: renderedObject.tags, overwrite: false) - } - - syntheticAmenity.copyNames(renderedObject) - if syntheticAmenity.getLocation() == nil { - syntheticAmenity.latitude = renderedObject.latitude - syntheticAmenity.longitude = renderedObject.longitude - } - - processPolygonCoordinates(x: renderedObject.x, y: renderedObject.y) - } + mergeObject(object, contentLocales: &contentLocales) } if contentLocales.count > 0 { syntheticAmenity.updateContentLocales(contentLocales) } - if objectCompleteness != .full { - objectCompleteness = syntheticAmenity.type != nil ? .combined : .empty + if objectCompleteness.rawValue < ObjectCompleteness.full.rawValue { + objectCompleteness = syntheticAmenity.type == nil ? .empty : .combined } if syntheticAmenity.type == nil { @@ -311,6 +276,41 @@ final class BaseDetailsObject: NSObject { } } + private func mergeObject(_ object: Any, contentLocales: inout Set) { + if let amenity = object as? OAPOI { + processAmenity(amenity, contentLocales: &contentLocales) + } else if let transportStop = object as? OATransportStop { + if let amenity = transportStop.poi { + processAmenity(amenity, contentLocales: &contentLocales) + } else { + processId(transportStop) + syntheticAmenity.copyNames(transportStop) + if syntheticAmenity.latitude == 0 && syntheticAmenity.longitude == 0 { + syntheticAmenity.latitude = transportStop.latitude + syntheticAmenity.longitude = transportStop.longitude + } + } + } else if let renderedObject = object as? OARenderedObject { + if let type = ObfConstants.getOsmEntityType(renderedObject) { + let osmId = ObfConstants.getOsmObjectId(renderedObject) + let objectId = ObfConstants.createMapObjectIdFromOsmId(osmId, type: type) + + if syntheticAmenity.obfId <= 0 && objectId > 0 { + syntheticAmenity.obfId = objectId + } + } + if syntheticAmenity.type == nil { + syntheticAmenity.copyAdditionalInfo(withMap: renderedObject.tags, overwrite: false) + } + syntheticAmenity.copyNames(renderedObject) + if syntheticAmenity.latitude == 0 && syntheticAmenity.longitude == 0 { + syntheticAmenity.latitude = renderedObject.latitude + syntheticAmenity.longitude = renderedObject.longitude + } + processPolygonCoordinates(x: renderedObject.x, y: renderedObject.y) + } + } + private func processId(_ object: OAMapObject?) { guard let object else { return } if (syntheticAmenity.obfId >= 0) && ObfConstants.isOsmUrlAvailable(object) { @@ -362,9 +362,9 @@ final class BaseDetailsObject: NSObject { syntheticAmenity.localizedContent = MutableOrderedDictionary() } - if amenity.localizedContent.count > 0 { + if let amenityContent = amenity.localizedContent, amenityContent.count > 0 { let localizedContent = MutableOrderedDictionary(dictionary: syntheticAmenity.localizedContent) - localizedContent.addEntries(from: amenity.localizedContent as! [NSString : NSString]) + localizedContent.addEntries(from: amenityContent as! [NSString : NSString]) syntheticAmenity.localizedContent = localizedContent } diff --git a/Sources/Controllers/Map/Helpers/OAMapSelectionHelper.mm b/Sources/Controllers/Map/Helpers/OAMapSelectionHelper.mm index 5718ac64ce..c0dbefffc5 100644 --- a/Sources/Controllers/Map/Helpers/OAMapSelectionHelper.mm +++ b/Sources/Controllers/Map/Helpers/OAMapSelectionHelper.mm @@ -78,8 +78,6 @@ - (MapSelectionResult *)collectObjectsFromMap:(CGPoint)point showUnknownLocation [self collectObjectsFromLayers:result unknownLocation:showUnknownLocation secondaryObjects:NO]; [self collectObjectsFromMap:result point:point]; - [self processTransportStops:result]; - if ([result isEmpty]) [self collectObjectsFromLayers:result unknownLocation:showUnknownLocation secondaryObjects:YES]; @@ -192,7 +190,7 @@ - (void)selectObjectsFromOpenGl:(MapSelectionResult *)result point:(CGPoint)poin [requestAmenity setLongitude:result.objectLatLon.coordinate.longitude]; OAAmenitySearcherRequest *request = [[OAAmenitySearcherRequest alloc] initWithMapObject:requestAmenity names:names]; - detailsObject = [amenitySearcher searchDetailedObject:request]; + detailsObject = [amenitySearcher searchDetailedObjectWithRequest:request]; } else { @@ -254,7 +252,7 @@ - (void)selectObjectsFromOpenGl:(MapSelectionResult *)result point:(CGPoint)poin else { OAAmenitySearcherRequest *request = [[OAAmenitySearcherRequest alloc] initWithMapObject:renderedObject]; - detailsObject = [amenitySearcher searchDetailedObject:request]; + detailsObject = [amenitySearcher searchDetailedObjectWithRequest:request]; if (detailsObject) { [detailsObject setMapIconName:[self getMapIconName:symbolInfo]]; @@ -619,75 +617,6 @@ - (BOOL)isTransportStop:(NSArray *)selectedObjects detail:( return _publicTransportTypes; } -- (void)processTransportStops:(MapSelectionResult *)result -{ - // Android has same code in contex menu UI init() - - // TODO: make this code async in part of next task https://github.com/osmandapp/OsmAnd-iOS/issues/4594 - // Step 1) ContextMenuLayer -> showContextMenu -> MapContextMenu.init() -> setSelectedObject(renderedObject) - // Step 2) RenderedObjectMenuBuilder -> build() -> searchAmenity() -> searchBaseDetailedObjectAsync() -> MapContextMenu.update() -> setSelectedObject(poi) - - NSMutableArray *selectedObjects = [result.allObjects mutableCopy]; - NSArray *publicTransportTypes = [self getPublicTransportTypes]; - if (publicTransportTypes) - { - NSMutableArray *transportStopAmenities = [NSMutableArray array]; - - for (SelectedMapObject *selectedObject in selectedObjects) - { - if ([selectedObject.object isKindOfClass:[OARenderedObject class]]) - { - OARenderedObject *renderedObject = selectedObject.object; - OAAmenitySearcherRequest *request = [[OAAmenitySearcherRequest alloc] initWithMapObject:renderedObject]; - BaseDetailsObject *detailsObject = [OAAmenitySearcher.sharedInstance searchDetailedObject:request]; - if (detailsObject) - { - selectedObject.object = detailsObject.syntheticAmenity; - } - else - { - OAPOI *poi = [RenderedObjectHelper getSyntheticAmenityWithRenderedObject:(OARenderedObject *)selectedObject.object]; - if (poi) - { - NSString *type = [ObfConstants getOsmEntityType:selectedObject.object]; - if (type) - { - int64_t osmId = [ObfConstants getOsmObjectId:selectedObject.object]; - int64_t poiObjectId = [ObfConstants createMapObjectIdFromOsmId:osmId type:type]; - poi.obfId = poiObjectId; - } - selectedObject.object = poi; - } - } - } - if ([selectedObject.object isKindOfClass:[OAPOI class]]) - { - OAPOI *amenity = (OAPOI *)selectedObject.object; - if (!NSStringIsEmpty(amenity.type.name) && [publicTransportTypes containsObject:amenity.type.name]) - [transportStopAmenities addObject:amenity]; - } - } - - if (!NSArrayIsEmpty(transportStopAmenities)) - { - for (OAPOI *amenity in transportStopAmenities) - { - OATransportStopsLayer *transportStopsLayer = [OARootViewController instance].mapPanel.mapViewController.mapLayers.transportStopsLayer; - OATransportStop *transportStop = [OATransportStopsBaseController findNearestTransportStopForAmenity:amenity]; - if (transportStop && transportStopsLayer) - { - SelectedMapObject *newTransportStop = [[SelectedMapObject alloc] initWithMapObject:transportStop provider:transportStopsLayer]; - [selectedObjects addObject:newTransportStop]; - - [selectedObjects filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SelectedMapObject *selectedObject, NSDictionary *bindings) { - return (!selectedObject && !amenity) || [amenity isEqual:selectedObject.object]; - }]]; - } - } - } - } -} - - (NSMutableArray *)getValues:(QHash)set { NSMutableArray *res = [NSMutableArray new]; diff --git a/Sources/Controllers/Map/Layers/OAContextMenuLayer.h b/Sources/Controllers/Map/Layers/OAContextMenuLayer.h index 05b341a648..53037ae838 100644 --- a/Sources/Controllers/Map/Layers/OAContextMenuLayer.h +++ b/Sources/Controllers/Map/Layers/OAContextMenuLayer.h @@ -30,7 +30,7 @@ - (void) hideContextPinMarker; - (BOOL) showContextMenu:(CGPoint)touchPoint showUnknownLocation:(BOOL)showUnknownLocation forceHide:(BOOL)forceHide; -- (void) showContextMenu:(CLLocation *)touchPointLatLon object:(SelectedMapObject *)selectedObject; +- (void) showContextMenuFor:(SelectedMapObject *)selectedObject latLon:(CLLocation *)latLon; - (OATargetPoint *) getUnknownTargetPoint:(double)latitude longitude:(double)longitude; diff --git a/Sources/Controllers/Map/Layers/OAContextMenuLayer.mm b/Sources/Controllers/Map/Layers/OAContextMenuLayer.mm index 7220ef5243..e5dec55acd 100644 --- a/Sources/Controllers/Map/Layers/OAContextMenuLayer.mm +++ b/Sources/Controllers/Map/Layers/OAContextMenuLayer.mm @@ -494,15 +494,11 @@ - (BOOL) showContextMenu:(CGPoint)touchPoint showUnknownLocation:(BOOL)showUnkno { SelectedMapObject *selectedObject = selectedObjects[0]; CLLocation *latLon = [result objectLatLon]; - if (!latLon || objectSelectionThreshold < 0 && selectedObject.provider) - { - id provider = selectedObject.provider; - latLon = [provider getObjectLocation:selectedObject]; - } - if (!latLon) - latLon = pointLatLon; - - [self showContextMenu:latLon object:selectedObject]; + + if (objectSelectionThreshold < 0) + latLon = nil; + + [self showContextMenuFor:selectedObject latLon:latLon touchPointLatLon:pointLatLon]; return YES; } else if (selectedObjects.count > 1) @@ -552,7 +548,12 @@ - (void) showContextMenuForSelectedObjects:(CLLocation *)touchPointLatLon select [mapPanel showContextMenuWithPoints:targetPoints selectedObjects:filteredSelectedObjects touchPointLatLon:touchPointLatLon]; } -- (void) showContextMenu:(CLLocation *)latLon object:(SelectedMapObject *)selectedObject +- (void) showContextMenuFor:(SelectedMapObject *)selectedObject latLon:(CLLocation *)latLon +{ + [self showContextMenuFor:selectedObject latLon:latLon touchPointLatLon:nil]; +} + +- (void) showContextMenuFor:(SelectedMapObject *)selectedObject latLon:(CLLocation *)latLon touchPointLatLon:(CLLocation *)pointLatLon { id selectedObj = selectedObject.object; OAPointDescription *pointDescription; @@ -561,28 +562,34 @@ - (void) showContextMenu:(CLLocation *)latLon object:(SelectedMapObject *)select if (provider) { if (!latLon) - { - latLon = [provider getObjectLocation:selectedObject]; - } + latLon = [provider getObjectLocation:selectedObj]; + pointDescription = [provider getObjectName:selectedObj]; } - [self showContextMenu:latLon pointDescription:pointDescription object:selectedObject provider:provider]; + + if (!latLon) + latLon = pointLatLon; + + [self showContextMenu:latLon pointDescription:pointDescription object:selectedObj selectedObject:selectedObject provider:provider touchPointLatLon:pointLatLon]; } -- (void) showContextMenu:(CLLocation *)latLon pointDescription:(OAPointDescription *)pointDescription object:(SelectedMapObject *)object provider:(id)provider +- (void) showContextMenu:(CLLocation *)latLon pointDescription:(OAPointDescription *)pointDescription object:(id)object selectedObject:(SelectedMapObject *)selectedObject provider:(id)provider touchPointLatLon:(CLLocation *)touchPointLatLon { if (!provider || ![provider showMenuAction:object]) { OATargetPoint *targetPoint; if (provider) - targetPoint = [provider getTargetPoint:object.object]; + targetPoint = [provider getTargetPoint:object]; else - targetPoint = [self.mapViewController.mapLayers.poiLayer getTargetPoint:object.object]; + targetPoint = [self.mapViewController.mapLayers.poiLayer getTargetPoint:object]; if (targetPoint) { + targetPoint.location = latLon.coordinate; [targetPoint initAdderssIfNeeded]; - [OARootViewController.instance.mapPanel showContextMenuWithPoints:@[targetPoint]]; + [targetPoint initDetailsObjectIfNeeded:selectedObject.object]; + + [OARootViewController.instance.mapPanel showContextMenuWithPoints:@[targetPoint] selectedObjects:@[selectedObject] touchPointLatLon:touchPointLatLon]; } } } diff --git a/Sources/Controllers/Map/Layers/OADestinationsLayer.mm b/Sources/Controllers/Map/Layers/OADestinationsLayer.mm index eb6952df95..b7d9b58173 100644 --- a/Sources/Controllers/Map/Layers/OADestinationsLayer.mm +++ b/Sources/Controllers/Map/Layers/OADestinationsLayer.mm @@ -517,7 +517,7 @@ - (OAPOI *)getMapObjectByMarker:(OADestination *)marker { NSString *mapObjName = [marker.mapObjectName componentsSeparatedByString:@"_"][0]; CLLocation *location = [[CLLocation alloc] initWithLatitude:marker.latitude longitude:marker.longitude]; - return [OAMapSelectionHelper findAmenity:location names:@[mapObjName] obfId:-1 radius:15]; + return [OAMapSelectionHelper findAmenity:location names:@[mapObjName] obfId:0 radius:15]; } return nil; } diff --git a/Sources/Controllers/Map/Layers/OAPOILayer.mm b/Sources/Controllers/Map/Layers/OAPOILayer.mm index fe7484256b..38d1e6ab82 100644 --- a/Sources/Controllers/Map/Layers/OAPOILayer.mm +++ b/Sources/Controllers/Map/Layers/OAPOILayer.mm @@ -603,7 +603,7 @@ - (BOOL) isTopPlace:(id)object else if ([object isKindOfClass:BaseDetailsObject.class]) placeId = ((BaseDetailsObject *)object).syntheticAmenity.obfId; - return placeId != -1 && _topPlaces[@(placeId)]; + return placeId > 0 && _topPlaces[@(placeId)]; } return NO; diff --git a/Sources/Controllers/OsmEditing/OAOsmEditTargetViewController.mm b/Sources/Controllers/OsmEditing/OAOsmEditTargetViewController.mm index 0fc5252eb8..21ccc04b3a 100644 --- a/Sources/Controllers/OsmEditing/OAOsmEditTargetViewController.mm +++ b/Sources/Controllers/OsmEditing/OAOsmEditTargetViewController.mm @@ -28,6 +28,8 @@ #import "OAOsmEditingPlugin.h" #import "OAPluginsHelper.h" +static const NSInteger kOrderInternalRow = 0; + @interface OAOsmEditTargetViewController () @end @@ -147,7 +149,7 @@ - (BOOL) showNearestWiki return NO; } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { NSString *prefLang = [OAUtilities preferredLang]; @@ -158,7 +160,7 @@ - (void) buildRows:(NSMutableArray *)rows && ![type isKindOfClass:[OAPOIMyLocationType class]]) { UIImage *icon = [type icon]; - [rows addObject:[[OARowInfo alloc] initWithKey:type.name icon:icon textPrefix:nil text:[_osmPoint getSubType] textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]]; + [rows addObject:[[OARowInfo alloc] initWithKey:type.name icon:icon textPrefix:nil text:[_osmPoint getSubType] textColor:nil isText:NO needLinks:NO order:kOrderInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]]; } [_osmPoint.getTags enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL * _Nonnull stop) { @@ -189,7 +191,7 @@ - (void) buildRows:(NSMutableArray *)rows if (!skip) { - [descriptions addObject:[[OARowInfo alloc] initWithKey:@"" icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:textPrefix text:[NSString stringWithFormat:@"%@=%@", key, value] textColor:nil isText:YES needLinks:YES order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]]; + [descriptions addObject:[[OARowInfo alloc] initWithKey:@"" icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:textPrefix text:[NSString stringWithFormat:@"%@=%@", key, value] textColor:nil isText:YES needLinks:YES order:kOrderInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]]; } }]; diff --git a/Sources/Controllers/OsmEditing/OAOsmNotesOnlineTargetViewController.mm b/Sources/Controllers/OsmEditing/OAOsmNotesOnlineTargetViewController.mm index 3ce0a5b14a..5e9b042300 100644 --- a/Sources/Controllers/OsmEditing/OAOsmNotesOnlineTargetViewController.mm +++ b/Sources/Controllers/OsmEditing/OAOsmNotesOnlineTargetViewController.mm @@ -24,6 +24,9 @@ #import "OAOnlineOsmNoteWrapper.h" #import "OAPluginsHelper.h" +static const NSInteger kOrderInternalRow = 0; + + @interface OAOsmNotesOnlineTargetViewController () @end @@ -127,13 +130,13 @@ - (BOOL) showNearestWiki return NO; } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { NSMutableArray *descriptions = [NSMutableArray array]; for (OACommentWrapper *cw in _point.comments) { - [descriptions addObject:[[OARowInfo alloc] initWithKey:@"" icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:nil text:[NSString stringWithFormat:@"%@ %@: %@", cw.date, cw.user, cw.text] textColor:nil isText:YES needLinks:YES order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]]; + [descriptions addObject:[[OARowInfo alloc] initWithKey:@"" icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:nil text:[NSString stringWithFormat:@"%@ %@: %@", cw.date, cw.user, cw.text] textColor:nil isText:YES needLinks:YES order:kOrderInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]]; } int i = 10000; diff --git a/Sources/Controllers/Panels/OAMapPanelViewController.mm b/Sources/Controllers/Panels/OAMapPanelViewController.mm index bdf02a26a0..99293c6e54 100644 --- a/Sources/Controllers/Panels/OAMapPanelViewController.mm +++ b/Sources/Controllers/Panels/OAMapPanelViewController.mm @@ -1374,25 +1374,21 @@ - (void) showContextMenuWithPoints:(NSArray *)targetPoints sele } } + if (validSelectedObjects && validSelectedObjects.count != validPoints.count) + validSelectedObjects = nil; + if (validPoints.count == 0) { return; } - if (selectedObjects.count == 1) - { - [contextLayer showContextMenu:touchPointLatLon object:selectedObjects[0]]; - } else if (validPoints.count == 1) { - [self showContextMenu:validPoints[0]]; + [self showContextMenu:validPoints[0] selectedObject:validSelectedObjects ? validSelectedObjects[0] : nil]; } else { for (OATargetPoint *targetPoint in validPoints) [self applyTargetPointController:targetPoint]; - - if (validSelectedObjects && validSelectedObjects.count != validPoints.count) - validSelectedObjects = nil; [self showMultiContextMenu:touchPointLatLon points:validPoints selectedObjects:validSelectedObjects]; } @@ -1420,6 +1416,11 @@ - (BOOL) isNewContextMenuDisabled } - (void)showContextMenu:(OATargetPoint *)targetPoint saveState:(BOOL)saveState preferredZoom:(float)preferredZoom +{ + [self showContextMenu:targetPoint saveState:saveState preferredZoom:preferredZoom selectedObject:nil]; +} + +- (void)showContextMenu:(OATargetPoint *)targetPoint saveState:(BOOL)saveState preferredZoom:(float)preferredZoom selectedObject:(SelectedMapObject *)selectedObject { if (_activeTargetType == OATargetGPX) [self hideScrollableHudViewController]; @@ -1454,6 +1455,8 @@ - (void)showContextMenu:(OATargetPoint *)targetPoint saveState:(BOOL)saveState p [self applyTargetPoint:targetPoint]; [_targetMenuView setTargetPoint:targetPoint]; + [_targetMenuView setSelectedObject:selectedObject.object]; + if ([targetPoint.targetObj isKindOfClass:OAMapObject.class]) { QVector points; @@ -1517,6 +1520,11 @@ - (void) onCancelNetworkGPX } - (void) showContextMenu:(OATargetPoint *)targetPoint +{ + [self showContextMenu:targetPoint selectedObject:nil]; +} + +- (void) showContextMenu:(OATargetPoint *)targetPoint selectedObject:(SelectedMapObject *)selectedObject { if (targetPoint.type == OATargetGPX) { @@ -1563,7 +1571,7 @@ - (void) showContextMenu:(OATargetPoint *)targetPoint } else { - [self showContextMenu:targetPoint saveState:YES preferredZoom:PREFERRED_FAVORITE_ZOOM]; + [self showContextMenu:targetPoint saveState:YES preferredZoom:PREFERRED_FAVORITE_ZOOM selectedObject:selectedObject]; } } @@ -2333,7 +2341,7 @@ - (void) showTargetPointMenu:(BOOL)saveMapState showFullMenu:(BOOL)showFullMenu _mapStateSaved = saveMapState; - OATargetMenuViewController *controller = [OATargetMenuViewController createMenuController:_targetMenuView.targetPoint activeTargetType:_activeTargetType activeViewControllerState:_activeViewControllerState headerOnly:NO]; + OATargetMenuViewController *controller = [OATargetMenuViewController createMenuController:_targetMenuView.targetPoint selectedObject:_targetMenuView.selectedObject activeTargetType:_activeTargetType activeViewControllerState:_activeViewControllerState headerOnly:NO]; BOOL prepared = NO; switch (_targetMenuView.targetPoint.type) { diff --git a/Sources/Controllers/TargetMenu/Favorites/OAFavoriteViewController.mm b/Sources/Controllers/TargetMenu/Favorites/OAFavoriteViewController.mm index 787354544a..b72d53220b 100644 --- a/Sources/Controllers/TargetMenu/Favorites/OAFavoriteViewController.mm +++ b/Sources/Controllers/TargetMenu/Favorites/OAFavoriteViewController.mm @@ -30,6 +30,10 @@ #include #include +static const NSInteger kOrderDescriptionRow = 0; +static const NSInteger kOrderFavGroupRow = 1; + + @implementation OAFavoriteViewController { OsmAndAppInstance _app; @@ -46,7 +50,7 @@ - (instancetype) initWithItem:(OAFavoriteItem *)favorite headerOnly:(BOOL)header _app = [OsmAndApp instance]; _favorite = favorite; _favoriteGroup = [OAFavoritesHelper getGroupByName:[self.favorite getCategory]]; - _openingHoursInfo = OpeningHoursParser::getInfo(self.favorite.favorite->getExtension(QString::fromNSString([PRIVATE_PREFIX stringByAppendingString:OPENING_HOURS_TAG])).toStdString()); + _openingHoursInfo = OpeningHoursParser::getInfo(self.favorite.favorite->getExtension(QString::fromNSString([AMENITY_PREFIX stringByAppendingString:OPENING_HOURS_TAG])).toStdString()); [self acquireOriginObject]; self.topToolbarType = ETopToolbarTypeMiddleFixed; @@ -63,15 +67,15 @@ - (void) acquireOriginObject _originObject = [_favorite getAmenity]; } -- (void) buildTopRows:(NSMutableArray *)rows +- (void) buildTopInternal:(NSMutableArray *)rows { - [super buildTopRows:rows]; + [super buildTopInternal:rows]; [self buildGroupFavouritesView:rows]; } -- (void) buildRowsInternal:(NSMutableArray *)rows +- (void) buildMenu:(NSMutableArray *)rows { - [self buildTopRows:rows]; + [self buildTopInternal:rows]; if (_favorite && [_favorite.getTimestamp timeIntervalSince1970] > 0) { @@ -82,7 +86,7 @@ - (void) buildRowsInternal:(NSMutableArray *)rows OAPOIViewController *builder = [[OAPOIViewController alloc] initWithPOI: _originObject]; builder.location = CLLocationCoordinate2DMake([_favorite getLatitude], [_favorite getLongitude]); NSMutableArray *internalRows = [NSMutableArray array]; - [builder buildRowsInternal:internalRows]; + [builder buildMenu:internalRows]; [rows addObjectsFromArray:internalRows]; } else @@ -98,7 +102,7 @@ - (void) buildDescription:(NSMutableArray *)rows NSString *desc = [_favorite getDescription]; if (desc && desc.length > 0) { - OARowInfo *descriptionRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:OALocalizedString(@"enter_description") text:desc textColor:nil isText:NO needLinks:NO order:0 typeName:kDescriptionRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *descriptionRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:OALocalizedString(@"enter_description") text:desc textColor:nil isText:NO needLinks:NO order:kOrderDescriptionRow typeName:kDescriptionRowType isPhoneNumber:NO isUrl:NO]; [rows addObject:descriptionRow]; } } @@ -115,7 +119,7 @@ - (void) buildGroupFavouritesView:(NSMutableArray *)rows NSString *name = [self.favorite getCategoryDisplayName]; NSString *description = OALocalizedString(@"context_menu_points_of_group"); - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:description text:name textColor:color isText:NO needLinks:NO order:1 typeName:kGroupRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:description text:name textColor:color isText:NO needLinks:NO order:kOrderFavGroupRow typeName:kGroupRowType isPhoneNumber:NO isUrl:NO]; rowInfo.collapsed = YES; rowInfo.collapsable = YES; rowInfo.height = 64; diff --git a/Sources/Controllers/TargetMenu/GPX/OAGPXWptViewController.mm b/Sources/Controllers/TargetMenu/GPX/OAGPXWptViewController.mm index ed5c1dc4c7..9b7f00e72f 100644 --- a/Sources/Controllers/TargetMenu/GPX/OAGPXWptViewController.mm +++ b/Sources/Controllers/TargetMenu/GPX/OAGPXWptViewController.mm @@ -32,6 +32,11 @@ #include #include +static const NSInteger kOrderDescriptionRow = 0; +static const NSInteger kOrderWptPointRow = 1; +static const NSInteger kOrderWptPointLinkRow = 2; + + @implementation OAGPXWptViewController { OsmAndAppInstance _app; @@ -52,7 +57,7 @@ - (id) initWithItem:(OAGpxWptItem *)wpt headerOnly:(BOOL)headerOnly } self.wpt = wpt; NSDictionary *extensions = [self.wpt.point getExtensionsToRead]; - NSString *key = [PRIVATE_PREFIX stringByAppendingString:OPENING_HOURS_TAG]; + NSString *key = [AMENITY_PREFIX stringByAppendingString:OPENING_HOURS_TAG]; NSString *openingHoursExt = extensions[key]; _openingHoursInfo = OpeningHoursParser::getInfo(openingHoursExt && openingHoursExt ? openingHoursExt.UTF8String : ""); [self acquireOriginObject]; @@ -72,9 +77,9 @@ - (void) acquireOriginObject _originObject = [_wpt getAmenity]; } -- (void) buildTopRows:(NSMutableArray *)rows +- (void) buildTopInternal:(NSMutableArray *)rows { - [super buildTopRows:rows]; + [super buildTopInternal:rows]; [self buildWaypointsView:rows]; } @@ -83,14 +88,14 @@ - (void) buildDescription:(NSMutableArray *)rows NSString *desc = [self getItemDesc]; if (desc && desc.length > 0) { - OARowInfo *descriptionRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:OALocalizedString(@"enter_description") text:desc textColor:nil isText:NO needLinks:NO order:0 typeName:kDescriptionRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *descriptionRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:OALocalizedString(@"enter_description") text:desc textColor:nil isText:NO needLinks:NO order:kOrderDescriptionRow typeName:kDescriptionRowType isPhoneNumber:NO isUrl:NO]; [rows addObject:descriptionRow]; } } -- (void) buildRowsInternal:(NSMutableArray *)rows +- (void) buildMenu:(NSMutableArray *)rows { - [self buildTopRows:rows]; + [self buildTopInternal:rows]; if ([self getTimestamp] && [[self getTimestamp] timeIntervalSince1970] > 0) { @@ -110,7 +115,7 @@ - (void) buildRowsInternal:(NSMutableArray *)rows textColor:UIColorFromRGB(kHyperlinkColor) isText:NO needLinks:YES - order:2 + order:kOrderWptPointLinkRow typeName:@"" isPhoneNumber:NO isUrl:YES]]; @@ -126,7 +131,7 @@ - (void) buildRowsInternal:(NSMutableArray *)rows OAPOIViewController *builder = [[OAPOIViewController alloc] initWithPOI: _originObject]; builder.location = CLLocationCoordinate2DMake(_wpt.point.lat, _wpt.point.lon); NSMutableArray *internalRows = [NSMutableArray array]; - [builder buildRowsInternal:internalRows]; + [builder buildMenu:internalRows]; [rows addObjectsFromArray:internalRows]; } else @@ -144,7 +149,7 @@ - (void) buildWaypointsView:(NSMutableArray *)rows UIColor *color = [self getItemColor]; UIImage *icon = [UIImage templateImageNamed:@"ic_custom_folder"]; - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:name text:gpxName textColor:color isText:NO needLinks:NO order:1 typeName:kGroupRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:name text:gpxName textColor:color isText:NO needLinks:NO order:kOrderWptPointRow typeName:kGroupRowType isPhoneNumber:NO isUrl:NO]; rowInfo.collapsed = YES; rowInfo.collapsable = YES; rowInfo.height = 64; diff --git a/Sources/Controllers/TargetMenu/MapDownload/OAMapDownloadController.mm b/Sources/Controllers/TargetMenu/MapDownload/OAMapDownloadController.mm index 99137c14e4..6fdfc1d632 100644 --- a/Sources/Controllers/TargetMenu/MapDownload/OAMapDownloadController.mm +++ b/Sources/Controllers/TargetMenu/MapDownload/OAMapDownloadController.mm @@ -23,6 +23,10 @@ #import "OASRTMPlugin.h" #import "OAPluginsHelper.h" +static const NSInteger kOrderRegionSizeRow = 1; +static const NSInteger kOrderRegionUrlRow = 2; +static const NSInteger kOrderRegionPopulationRow = 3; + @interface OAMapDownloadController () @end @@ -205,7 +209,7 @@ - (id) getTargetObj return _mapObject; } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { OAWorldRegion *region = _mapObject.worldRegion; OAResourceItem *item = _mapObject.indexItem; @@ -218,7 +222,7 @@ - (void) buildRows:(NSMutableArray *)rows if (item.sizePkg && item.sizePkg > 0) rowText = [NSString stringWithFormat:@"%@ - %@", rowText, [NSByteCountFormatter stringFromByteCount:item.sizePkg countStyle:NSByteCountFormatterCountStyleFile]]; - [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:nil text:rowText textColor:nil isText:NO needLinks:NO order:1 typeName:@"" isPhoneNumber:NO isUrl:NO]]; + [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:nil text:rowText textColor:nil isText:NO needLinks:NO order:kOrderRegionSizeRow typeName:@"" isPhoneNumber:NO isUrl:NO]]; } if (region.wikiLink && region.wikiLink.length > 0) { @@ -228,11 +232,11 @@ - (void) buildRows:(NSMutableArray *)rows url = [NSString stringWithFormat:@"https://%@.wikipedia.org/wiki/%@", items[0], [items[1] stringByReplacingOccurrencesOfString:@" " withString:@"_"]]; else url = [NSString stringWithFormat:@"https://wikipedia.org/wiki/%@", [items[0] stringByReplacingOccurrencesOfString:@" " withString:@"_"]]; - [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:nil text:url textColor:UIColorFromRGB(kHyperlinkColor) isText:NO needLinks:YES order:2 typeName:@"" isPhoneNumber:NO isUrl:YES]]; + [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:nil text:url textColor:UIColorFromRGB(kHyperlinkColor) isText:NO needLinks:YES order:kOrderRegionUrlRow typeName:@"" isPhoneNumber:NO isUrl:YES]]; } if (region.population && region.population.length > 0) { - [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:OALocalizedString(@"population_num") text:[OAOsmAndFormatter getFormattedOsmTagValue:region.population] textColor:nil isText:YES needLinks:NO order:3 typeName:@"" isPhoneNumber:NO isUrl:NO]]; + [rows addObject:[[OARowInfo alloc] initWithKey:region.name icon:[OATargetInfoViewController getIcon:iconInfo] textPrefix:OALocalizedString(@"population_num") text:[OAOsmAndFormatter getFormattedOsmTagValue:region.population] textColor:nil isText:YES needLinks:NO order:kOrderRegionPopulationRow typeName:@"" isPhoneNumber:NO isUrl:NO]]; } } diff --git a/Sources/Controllers/TargetMenu/OATargetInfoViewController.h b/Sources/Controllers/TargetMenu/OATargetInfoViewController.h index f4bfc50bcb..9448472534 100644 --- a/Sources/Controllers/TargetMenu/OATargetInfoViewController.h +++ b/Sources/Controllers/TargetMenu/OATargetInfoViewController.h @@ -22,10 +22,10 @@ @property (nonatomic) NSArray *additionalRows; - (BOOL) needCoords; -- (void) buildTopRows:(NSMutableArray *)rows; +- (void) buildTopInternal:(NSMutableArray *)rows; - (void) buildDescription:(NSMutableArray *)rows; -- (void) buildRows:(NSMutableArray *)rows; -- (void) buildRowsInternal:(NSMutableArray *)rows; +- (void) buildInternal:(NSMutableArray *)rows; +- (void) buildMenu:(NSMutableArray *)rows; - (void) buildDateRow:(NSMutableArray *)rows timestamp:(NSDate *)timestamp; - (void) buildCommentRow:(NSMutableArray *)rows comment:(NSString *)comment; - (void) buildCoordinateRows:(NSMutableArray *)rows; diff --git a/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm b/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm index 62834e004f..f98a2bc49b 100644 --- a/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm +++ b/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm @@ -92,6 +92,20 @@ static const CGFloat kTextMaxHeight = 150.0; +static const NSInteger kOrderPhotoRow = -102; +static const NSInteger kOrderMapillaryRow = -101; +static const NSInteger kOrderWithinRow = -100; +static const NSInteger kOrderTopInternalRow = 0; +static const NSInteger kOrderDescriptionRow = 0; +static const NSInteger kOrderInternalRow = 0; +static const NSInteger kOrderDetailsRow = 0; +static const NSInteger kOrderDateRow = 3; +static const NSInteger kOrderCoommentRow = 4; +static const NSInteger kOrderPoiRow = 1000; +static const NSInteger kOrderCoordinatesRow = 20000; +static const NSInteger kOrderPhotoEmptyRow = 30001; +static const NSInteger kOrderMapillaryEmptyRow = 30002; + @interface OATargetInfoViewController() @property (nonatomic) BOOL wikiCardsReady; @@ -161,14 +175,14 @@ + (UIImage *) getIcon:(NSString *)fileName size:(CGSize)size return img; } -- (void) buildTopRows:(NSMutableArray *)rows +- (void) buildTopInternal:(NSMutableArray *)rows { [self buildDescription:rows]; NSArray *localTransportRoutes = [self getLocalTransportStopRoutes]; NSArray *nearbyTransportRoutes = [self getNearbyTransportStopRoutes]; if (localTransportRoutes.count > 0) { - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:OALocalizedString(@"transport_Routes") textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:OALocalizedString(@"transport_Routes") textColor:nil isText:NO needLinks:NO order:kOrderTopInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]; rowInfo.collapsable = YES; rowInfo.collapsed = NO; rowInfo.collapsableView = [[OACollapsableTransportStopRoutesView alloc] initWithFrame:CGRectMake([OAUtilities getLeftMargin], 0, 320, 100)]; @@ -178,7 +192,7 @@ - (void) buildTopRows:(NSMutableArray *)rows if (nearbyTransportRoutes.count > 0) { NSString *routesWithingDistance = [NSString stringWithFormat:@"%@ %@", OALocalizedString(@"transport_nearby_routes_within"), [OAOsmAndFormatter getFormattedDistance:kShowStopsRadiusMeters]]; - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:routesWithingDistance textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:routesWithingDistance textColor:nil isText:NO needLinks:NO order:kOrderTopInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]; rowInfo.collapsable = YES; rowInfo.collapsed = NO; rowInfo.collapsableView = [[OACollapsableTransportStopRoutesView alloc] initWithFrame:CGRectMake([OAUtilities getLeftMargin], 0, 320, 100)]; @@ -192,7 +206,7 @@ - (void) buildDescription:(NSMutableArray *)rows // implement in subclasses } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { // implement in subclasses } @@ -201,35 +215,55 @@ - (void) appdendDetailsButtonRow:(NSMutableArray *)rows { if ([self showDetailsButton]) { - OARowInfo *collapseDetailsRowCell = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:nil] textPrefix:nil text:@"" textColor:nil isText:NO needLinks:NO order:0 typeName:kCollapseDetailsRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *collapseDetailsRowCell = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:nil] textPrefix:nil text:@"" textColor:nil isText:NO needLinks:NO order:kOrderDetailsRow typeName:kCollapseDetailsRowType isPhoneNumber:NO isUrl:NO]; [collapseDetailsRowCell setHeight:[self detailsButtonHeight]]; [rows addObject:collapseDetailsRowCell]; } } -- (void) buildRowsInternal:(NSMutableArray *)rows +- (void) buildMenu:(NSMutableArray *)rows { + //TODO: clear code + _rows = rows; - [self buildTopRows:_rows]; + [self buildTopInternal:_rows]; + + + // if (showTitleIfTruncated) { + // buildTitleRow(view); + // } + // ?? don't exist in android [self appdendDetailsButtonRow:_rows]; + + [self buildWithinRow]; + [self buildNearestRows]; + + // if (needBuildPlainMenuItems()) { + // buildPlainMenuItems(view); + // } - [self buildRows:_rows]; + [self buildInternal:_rows]; + + [self buildPluginRows]; + // ?? don't exist in android if (self.additionalRows) { [_rows addObjectsFromArray:self.additionalRows]; } - - if ([self showNearestWiki] && !OAIAPHelper.sharedInstance.wiki.disabled && [OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]) - [self buildRowsPoi:YES]; - - if ([self showNearestPoi]) - [self buildRowsPoi:NO]; + [self buildCoordinateRows:rows]; + [self buildPhotosRow]; + + // ?? don't exist in android. move to buildPluginRows() +// [self addMapillaryCardsRowInfoIfNeeded]; + + + [_rows sortUsingComparator:^NSComparisonResult(OARowInfo *row1, OARowInfo *row2) { if (row1.order < row2.order) return NSOrderedAscending; @@ -238,12 +272,9 @@ - (void) buildRowsInternal:(NSMutableArray *)rows else return NSOrderedDescending; }]; - - [self buildCoordinateRows:rows]; - [self addNearbyImagesIfNeeded]; - [self addMapillaryCardsRowInfoIfNeeded]; - [self startLoadingImages]; + +// [self startLoadingImages]; _calculatedWidth = 0; [self contentHeight:self.tableView.bounds.size.width]; @@ -289,7 +320,7 @@ - (void)buildWithinRow textColor:nil isText:YES needLinks:YES - order:-1 + order:kOrderWithinRow typeName:WITHIN_POLYGONS_ROW_KEY isPhoneNumber:NO isUrl:NO]; @@ -372,12 +403,12 @@ - (void)buildRowsPoi:(BOOL)isWiki if (nearest.count > 0) { UIImage *icon = isWiki ? [UIImage mapSvgImageNamed:@"mx_wiki_place"] : poi.icon; - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:nil text:rowText textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:nil icon:icon textPrefix:nil text:rowText textColor:nil isText:NO needLinks:NO order:kOrderPoiRow typeName:@"" isPhoneNumber:NO isUrl:NO]; rowInfo.collapsable = YES; rowInfo.collapsed = YES; rowInfo.collapsableView = [[OACollapsableNearestPoiWikiView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; [((OACollapsableNearestPoiWikiView *) rowInfo.collapsableView) setData:nearest hasItems:(isWiki ? _hasOsmWiki : YES) latitude:self.location.latitude longitude:self.location.longitude filter:filter]; - rowInfo.order = 1000; + rowInfo.order = kOrderPoiRow; [_rows addObject:rowInfo]; } } @@ -391,7 +422,7 @@ - (void) buildDateRow:(NSMutableArray *)rows timestamp:(NSDate *)ti dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterShortStyle; NSString *formattedDate = [dateFormatter stringFromDate:timestamp]; - OARowInfo *dateRowCell = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:@"ic_custom_date"] textPrefix:nil text:formattedDate textColor:nil isText:NO needLinks:NO order:3 typeName:kTimestampRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *dateRowCell = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:@"ic_custom_date"] textPrefix:nil text:formattedDate textColor:nil isText:NO needLinks:NO order:kOrderDateRow typeName:kTimestampRowType isPhoneNumber:NO isUrl:NO]; [rows addObject:dateRowCell]; } } @@ -400,7 +431,7 @@ - (void) buildCommentRow:(NSMutableArray *)rows comment:(NSString * { if (comment.length > 0) { - OARowInfo *commentRow = [[OARowInfo alloc] initWithKey:nil icon:[UIImage imageNamed:@"ic_description"] textPrefix:nil text:comment textColor:nil isText:YES needLinks:NO order:4 typeName:kCommentRowType isPhoneNumber:NO isUrl:NO]; + OARowInfo *commentRow = [[OARowInfo alloc] initWithKey:nil icon:[UIImage imageNamed:@"ic_description"] textPrefix:nil text:comment textColor:nil isText:YES needLinks:NO order:kOrderCoommentRow typeName:kCommentRowType isPhoneNumber:NO isUrl:NO]; [rows addObject:commentRow]; } } @@ -409,7 +440,7 @@ - (void)buildCoordinateRows:(NSMutableArray *)rows { if ([self needCoords]) { - OARowInfo *coordinatesRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:@"" textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo *coordinatesRow = [[OARowInfo alloc] initWithKey:nil icon:nil textPrefix:nil text:@"" textColor:nil isText:NO needLinks:NO order:kOrderCoordinatesRow typeName:@"" isPhoneNumber:NO isUrl:NO]; coordinatesRow.collapsed = YES; coordinatesRow.collapsable = YES; OACollapsableCoordinatesView *collapsableView = [[OACollapsableCoordinatesView alloc] initWithFrame:CGRectMake(0, 0, 320, 100) lat:self.location.latitude lon:self.location.longitude]; @@ -418,6 +449,37 @@ - (void)buildCoordinateRows:(NSMutableArray *)rows } } +- (void)buildNearestRows +{ + [self buildNearestWikiRow]; + [self buildNearestPoiRow]; + [self buildRouteRows]; +} + +- (void)buildNearestWikiRow +{ + if ([self showNearestWiki] && !OAIAPHelper.sharedInstance.wiki.disabled && [OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]) + [self buildRowsPoi:YES]; +} + +- (void)buildNearestPoiRow +{ + if ([self showNearestPoi]) + [self buildRowsPoi:NO]; +} + +- (void)buildRouteRows +{ + // TODO: implement +} + +- (void)buildPluginRows +{ + // TODO: implement + + [self addMapillaryCardsRowInfoIfNeeded]; +} + - (void) calculateRowsHeight:(CGFloat)width { CGFloat regularTextWidth = width - kMarginLeft - kMarginRight; @@ -489,7 +551,7 @@ - (void) viewDidLoad self.tableView.backgroundView = view; self.tableView.scrollEnabled = NO; _calculatedWidth = 0; - [self buildRowsInternal:[NSMutableArray array]]; + [self buildMenu:[NSMutableArray array]]; } - (void) didReceiveMemoryWarning @@ -541,7 +603,7 @@ - (void) setContentBackgroundColor:(UIColor *)color - (void) rebuildRows { - [self buildRowsInternal:[NSMutableArray array]]; + [self buildMenu:[NSMutableArray array]]; } - (void)processNearestWiki:(OAPOI *)poi @@ -833,10 +895,13 @@ - (void)reorderCards:(NSMutableArray *)cards { [cards setArray:uniqueOrderedSet.array]; } -- (void)addNearbyImagesIfNeeded +- (void)buildPhotosRow { - OARowInfo *nearbyImagesRowInfo = [[OARowInfo alloc] initWithKey:nil icon:[UIImage imageNamed:@"ic_custom_photo"] textPrefix:nil text:OALocalizedString(@"online_photos") textColor:nil isText:NO needLinks:NO order:0 typeName:@"" isPhoneNumber:NO isUrl:NO]; - + BOOL hasPhoto = YES; //TODO: implement later. Move emmty row to bottom of context menu + NSInteger order = hasPhoto ? kOrderPhotoRow : kOrderPhotoEmptyRow; + + OARowInfo *nearbyImagesRowInfo = [[OARowInfo alloc] initWithKey:nil icon:[UIImage imageNamed:@"ic_custom_photo"] textPrefix:nil text:OALocalizedString(@"online_photos") textColor:nil isText:NO needLinks:NO order:order typeName:@"" isPhoneNumber:NO isUrl:NO]; + CollapsableCardsView *cardView = [CollapsableCardsView new]; cardView.contentType = CollapsableCardsTypeOnlinePhoto; cardView.delegate = self; @@ -848,13 +913,18 @@ - (void)addNearbyImagesIfNeeded [self clearContentForRowInfo:_onlinePhotoCardsRowInfo]; _onlinePhotoCardsRowInfo = nearbyImagesRowInfo; + + [self startLoadingImages]; } - (void)addMapillaryCardsRowInfoIfNeeded { - OAMapillaryPlugin *plagin = (OAMapillaryPlugin *) [OAPluginsHelper getPlugin:OAMapillaryPlugin.class]; - if ([plagin isEnabled]) + OAMapillaryPlugin *plugin = (OAMapillaryPlugin *) [OAPluginsHelper getPlugin:OAMapillaryPlugin.class]; + if ([plugin isEnabled]) { + BOOL hasPhoto = YES; //TODO: implement later. Move emmty row to bottom of context menu + NSInteger order = hasPhoto ? kOrderMapillaryRow : kOrderMapillaryEmptyRow; + OARowInfo *mapillaryCardsRowInfo = [[OARowInfo alloc] initWithKey:nil icon:[UIImage imageNamed:@"ic_custom_photo_street"] textPrefix:nil @@ -862,7 +932,7 @@ - (void)addMapillaryCardsRowInfoIfNeeded textColor:nil isText:NO needLinks:NO - order:0 + order:order typeName:@"" isPhoneNumber:NO isUrl:NO]; diff --git a/Sources/Controllers/TargetMenu/OATargetMenuViewController.h b/Sources/Controllers/TargetMenu/OATargetMenuViewController.h index d040d6113a..d1e88f35e9 100644 --- a/Sources/Controllers/TargetMenu/OATargetMenuViewController.h +++ b/Sources/Controllers/TargetMenu/OATargetMenuViewController.h @@ -113,6 +113,8 @@ typedef void (^ContentHeightChangeListenerBlock)(CGFloat newHeight); + (OATargetMenuViewController *) createMenuController:(OATargetPoint *)targetPoint activeTargetType:(OATargetPointType)activeTargetType activeViewControllerState:(OATargetMenuViewControllerState *)activeViewControllerState headerOnly:(BOOL)headerOnly; ++ (OATargetMenuViewController *) createMenuController:(OATargetPoint *)targetPoint selectedObject:(id)selectedObject activeTargetType:(OATargetPointType)activeTargetType activeViewControllerState:(OATargetMenuViewControllerState *)activeViewControllerState headerOnly:(BOOL)headerOnly; + - (id) getTargetObj; - (nullable UIImage *) getIcon; diff --git a/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm b/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm index ad05bad08a..5cd3dd476e 100644 --- a/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm +++ b/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm @@ -92,6 +92,11 @@ @implementation OATargetMenuViewController } + (OATargetMenuViewController *) createMenuController:(OATargetPoint *)targetPoint activeTargetType:(OATargetPointType)activeTargetType activeViewControllerState:(OATargetMenuViewControllerState *)activeViewControllerState headerOnly:(BOOL)headerOnly +{ + return [self.class createMenuController:targetPoint selectedObject:nil activeTargetType:activeTargetType activeViewControllerState:activeViewControllerState headerOnly:headerOnly]; +} + ++ (OATargetMenuViewController *) createMenuController:(OATargetPoint *)targetPoint selectedObject:(id)selectedObject activeTargetType:(OATargetPointType)activeTargetType activeViewControllerState:(OATargetMenuViewControllerState *)activeViewControllerState headerOnly:(BOOL)headerOnly { double lat = targetPoint.location.latitude; double lon = targetPoint.location.longitude; @@ -154,7 +159,21 @@ + (OATargetMenuViewController *) createMenuController:(OATargetPoint *)targetPoi case OATargetPOI: { - controller = [[OAPOIViewController alloc] initWithPOI:targetPoint.targetObj]; + if (selectedObject) + { + if ([selectedObject isKindOfClass:BaseDetailsObject.class]) + { + BaseDetailsObject *detailsObject = [OAAmenitySearcher.sharedInstance searchDetailedObject:selectedObject]; + if (detailsObject) + { + controller = [[PlaceDetailsViewController alloc] initWithPoi:targetPoint.targetObj detailsObject:detailsObject]; + } + } + } + else + { + controller = [[OAPOIViewController alloc] initWithPOI:targetPoint.targetObj]; + } break; } diff --git a/Sources/Controllers/TargetMenu/POI/AdditionalInfoBundle.swift b/Sources/Controllers/TargetMenu/POI/AdditionalInfoBundle.swift new file mode 100644 index 0000000000..c5740cbe43 --- /dev/null +++ b/Sources/Controllers/TargetMenu/POI/AdditionalInfoBundle.swift @@ -0,0 +1,91 @@ +// +// AdditionalInfoBundle.swift +// OsmAnd +// +// Created by Max Kojin on 02/10/25. +// Copyright © 2025 OsmAnd. All rights reserved. +// + +@objcMembers +public class AdditionalInfoBundle: NSObject { + + private static let HIDDEN_EXTENSIONS: [String] = [COLOR_NAME_EXTENSION_KEY, ICON_NAME_EXTENSION_KEY, BACKGROUND_TYPE_EXTENSION_KEY, PROFILE_TYPE_EXTENSION_KEY, ADDRESS_EXTENSION_KEY, AMENITY_ORIGIN_EXTENSION_KEY, POITYPE, SUBTYPE] + + private let additionalInfo: [String: String]? + private var filteredAdditionalInfo: [String: String]? + private var localizedAdditionalInfo: [String: Any]? + private var customHiddenExtensions: [String] = [] + + init(additionalInfo: [String: String]) { + self.additionalInfo = additionalInfo + } + + func getFilteredLocalizedInfo() -> [String: Any] { + if localizedAdditionalInfo == nil { + localizedAdditionalInfo = MergeLocalizedTagsAlgorithm.shared.execute(originalDict: getFilteredInfo() ?? [:]) + } + return localizedAdditionalInfo ?? [:] + } + + func getFilteredInfo() -> [String: String]? { + if filteredAdditionalInfo == nil { + var result = [String: String]() + for origKey in getAdditionalInfoKeys() { + var key: String? + if origKey == AMENITY_PREFIX + OPENING_HOURS_TAG { + key = origKey.replacingOccurrences(of: AMENITY_PREFIX, with: "") + } else if origKey.hasPrefix(AMENITY_PREFIX) { + continue + } else { + key = origKey.replacingOccurrences(of: OSM_PREFIX_KEY, with: "") + } + + if let key { + if !Self.HIDDEN_EXTENSIONS.contains(key) && + (customHiddenExtensions.isEmpty || !customHiddenExtensions.contains(key)) { + result[key] = get(key) + } + } + } + + filteredAdditionalInfo = result + } + return filteredAdditionalInfo + } + + func containsAny(_ keys: [String]) -> Bool { + for key in keys { + if contains(key) { + return true + } + } + return false + } + + func contains(_ key: String) -> Bool { + getAdditionalInfoKeys().contains(key) + } + + func getAdditionalInfoKeys() -> [String] { + if let additionalInfo { + return Array(additionalInfo.keys) + } + return [] + } + + func get(_ key: String) -> String? { + if let additionalInfo { + let str = additionalInfo[key] + // TODO: implement if needed + // str = Amenity.unzipContent(str); + return str + } + return nil + } + + func setCustomHiddenExtensions(_ extensions: [String]) { + filteredAdditionalInfo = nil + localizedAdditionalInfo = nil + customHiddenExtensions = extensions + } +} diff --git a/Sources/Controllers/TargetMenu/POI/AmenityUIHelper.swift b/Sources/Controllers/TargetMenu/POI/AmenityUIHelper.swift new file mode 100644 index 0000000000..38b3c680a1 --- /dev/null +++ b/Sources/Controllers/TargetMenu/POI/AmenityUIHelper.swift @@ -0,0 +1,83 @@ +// +// AmenityUIHelper.swift +// OsmAnd +// +// Created by Max Kojin on 02/10/25. +// Copyright © 2025 OsmAnd. All rights reserved. +// + +@objcMembers +final class AmenityUIHelper: NSObject { + private static let CUISINE_INFO_ID = COLLAPSABLE_PREFIX + "cuisine" + private static let DISH_INFO_ID = COLLAPSABLE_PREFIX + "dish" + private static let US_MAPS_RECREATION_AREA = "us_maps_recreation_area" + + private var additionalInfo: AdditionalInfoBundle + + private var preferredLang: String + private var wikiAmenity: OAPOI? + private var poiTypes: OAPOIType? + private var poiCategory: OAPOICategory? + private var poiType: OAPOIType? + private var subtype: String? + + //private var cuisineRow: AmenityInfoRow? + private var poiAdditionalCategories = [String: [OAPOIType]]() + private var collectedPoiTypes = [String: [OAPOIType]]() + private var osmEditingEnabled = true // PluginsHelper.isActive(OsmEditingPlugin.class); + private var lastBuiltRowIsDescription = false + + init(preferredLang: String, infoBundle: AdditionalInfoBundle) { + self.preferredLang = preferredLang + self.additionalInfo = infoBundle + super.init() + } + + func setPreferredLang(_ lang: String) { + preferredLang = lang + } + + func buildInternal() { + // TODO: implement + } + + func buildWikiDataRow() { + // TODO: implement + } + + func initVariables() { + // TODO: implement + } + + // private void sortInfoRows(@NonNull List infoRows) { + + // private AmenityInfoRow createLocalizedAmenityInfoRow(@NonNull Context context, @NonNull String key, @NonNull Object vl) { + + // private AmenityInfoRow createPoiAdditionalInfoRow(@NonNull Context context, + + // private boolean isKeyToSkip(@NonNull String key) { + + // private PoiType fetchPoiAdditionalType(@NonNull String key, @Nullable String vl) { + + // public void buildNamesRow(ViewGroup viewGroup, Map namesMap, boolean altName) { + + // protected CollapsableView getNamesCollapsableView(@NonNull Map mapNames, + + // private void sortDescriptionRows(@NonNull List descriptions) { + + // public static String getSocialMediaUrl(String key, String value) { + + // private void buildRow(View view, int iconId, String text, String textPrefix, String hiddenUrl, + + // protected void buildRow(View view, Drawable icon, String text, String textPrefix, + + // public void buildAmenityRow(View view, AmenityInfoRow info) { + + // private CollapsableView getPoiTypeCollapsableView(Context context, boolean collapsed, + + // public static Set collectAvailableLocalesFromTags(@NonNull Collection tags) { + + // private Locale getPreferredLocale(Collection locales) { + + // public static Pair getDescriptionWithPreferredLang(@NonNull OsmandApplication app, +} diff --git a/Sources/Controllers/TargetMenu/POI/MergeLocalizedTagsAlgorithm.swift b/Sources/Controllers/TargetMenu/POI/MergeLocalizedTagsAlgorithm.swift new file mode 100644 index 0000000000..219a9544b6 --- /dev/null +++ b/Sources/Controllers/TargetMenu/POI/MergeLocalizedTagsAlgorithm.swift @@ -0,0 +1,164 @@ +// +// MergeLocalizedTagsAlgorithm.swift +// OsmAnd +// +// Created by Max Kojin on 03/10/25. +// Copyright © 2025 OsmAnd. All rights reserved. +// + +@objcMembers +final class MergeLocalizedTagsAlgorithm: NSObject { + + private let NAME_TAG_PREFIXES = ["name", "int_name", "nat_name", "reg_name", "loc_name", + "old_name", "alt_name", "short_name", "official_name", "lock_name"] + + private override init() {} + + static var shared: MergeLocalizedTagsAlgorithm = { + let instance = MergeLocalizedTagsAlgorithm() + return instance + }() + + func execute(originalDict: [String: Any]) -> [String: Any] { + executeImpl(originalDict) + } + + private func executeImpl(_ originalDict: [String: Any]) -> [String: Any] { + var resultDict: [String: Any] = [:] + let langDict = applyLanguageOverride(originalDict) + var localizationsDict: [String: [String: Any]] = [:] + + for key in langDict.keys { + let converted = convertKey(key) + processAdditionalTypeWithKey(key: key, convertedKey: converted, originalDict: langDict, localizationsDict: &localizationsDict, resultDict: &resultDict) + } + + let keysToUpdate = findKeysToUpdate(localizationsDict) + for baseKey in keysToUpdate { + var localizations = localizationsDict[baseKey] + + if let value = langDict[baseKey] { + localizations?[baseKey] = value + } else { + continue + } + } + + var finalDict = finalizeLocalizationDict(localizationsDict) + addRemainingEntriesFrom(resultDict, into: &finalDict) + return finalDict + } + + private func applyLanguageOverride(_ originalDict: [String: Any]) -> [String: Any] { + var processedDict = originalDict + + let langYesPrefix = LANG_YES + ":" + let langKeys = processedDict.keys.filter { $0.hasPrefix(langYesPrefix) && processedDict[$0] as! String == "yes" } + + if langKeys.count == 1, let langKey = langKeys.first { + let langCode = String(langKey.dropFirst(langYesPrefix.count)) + + renameTagInMap(&processedDict, oldKey: POI_NAME, newKey: POI_NAME + ":" + langCode) + renameTagInMap(&processedDict, oldKey: DESCRIPTION_TAG, newKey: DESCRIPTION_TAG + ":" + langCode) + } + return processedDict + } + + private func renameTagInMap(_ dict: inout [String: Any], oldKey: String, newKey: String) { + if let value = dict.removeValue(forKey: oldKey) { + dict[newKey] = value + } + } + + private func processNameTagWithKey(key: String, convertedKey: String, originalDict: [String: Any], localizationsDict: inout [String: [String: Any]]) { + if key.contains(":") { + let components = convertedKey.components(separatedBy: ":") + if components.count == 2 { + let baseKey = components[0] + let localeKey = "\(baseKey):\(components[1])" + + if let value = originalDict[convertedKey] { + var nameDict = dictionaryForKey(key: "name", dict: &localizationsDict) + nameDict[localeKey] = value + } + } + } else { + if let value = originalDict[key] { + var nameDict = dictionaryForKey(key: "name", dict: &localizationsDict) + nameDict[convertedKey] = value + } + } + } + + private func processAdditionalTypeWithKey(key: String, convertedKey: String, originalDict: [String: Any], localizationsDict: inout [String: [String: Any]], resultDict: inout [String: Any]) { + let poiType = OAPOIHelper.sharedInstance().getAnyPoiAdditionalType(byKey: convertedKey) + + if poiType?.lang != nil, key.contains(":") { + let components = key.components(separatedBy: ":") + if components.count == 2 { + let baseKey = components[0] + let localeKey = "\(baseKey):\(components[1])" + + if let value = originalDict[key] { + var baseDict = dictionaryForKey(key: baseKey, dict: &localizationsDict) + baseDict[localeKey] = value + } + } + } else { + if let value = originalDict[key] { + resultDict[key] = value + } + } + } + + private func dictionaryForKey(key: String, dict: inout [String: [String: Any]]) -> [String: Any] { + if dict[key] == nil { + dict[key] = [:] + } + return dict[key] ?? [:] + } + + private func findKeysToUpdate(_ localizationsDict: [String: [String: Any]]) -> [String] { + var keysToUpdate = [String]() + for baseKey in localizationsDict.keys { + if let localizations = localizationsDict[baseKey], localizations[baseKey] == nil { + keysToUpdate.append(baseKey) + } + } + return keysToUpdate + } + + private func finalizeLocalizationDict(_ localizationsDict: [String: [String: Any]]) -> [String: Any] { + var finalDict: [String: Any] = [:] + + for baseKey in localizationsDict.keys { + var entryDict = [String: Any]() + if let localizations = localizationsDict[baseKey] { + entryDict["localizations"] = localizations + } + finalDict[baseKey] = entryDict + } + return finalDict + } + + private func addRemainingEntriesFrom(_ resultDict: [String: Any], into finalDict: inout [String: Any]) { + for (key, value) in resultDict { + if finalDict[key] == nil { + finalDict[key] = value + } + } + } + + private func isNameTag(_ tag: String) -> Bool { + for prefix in NAME_TAG_PREFIXES { + if tag.hasPrefix(prefix) { + return true + } + } + return false + } + + private func convertKey(_ key: String) -> String { + key.replacingOccurrences(of: XML_COLON, with: ":") + } +} diff --git a/Sources/Controllers/TargetMenu/POI/OAPOIViewController.h b/Sources/Controllers/TargetMenu/POI/OAPOIViewController.h index 24d3614d9e..c8bc685689 100644 --- a/Sources/Controllers/TargetMenu/POI/OAPOIViewController.h +++ b/Sources/Controllers/TargetMenu/POI/OAPOIViewController.h @@ -14,6 +14,7 @@ - (id) initWithPOI:(OAPOI *)poi; +- (void) setObject:(id)object; - (void) setup:(OAPOI *)poi; - (NSString *) getOsmUrl; diff --git a/Sources/Controllers/TargetMenu/POI/OAPOIViewController.mm b/Sources/Controllers/TargetMenu/POI/OAPOIViewController.mm index 69c2b2b332..def0fce80d 100644 --- a/Sources/Controllers/TargetMenu/POI/OAPOIViewController.mm +++ b/Sources/Controllers/TargetMenu/POI/OAPOIViewController.mm @@ -43,6 +43,10 @@ static const NSInteger WAY_MODULO_REMAINDER = 1; +static const NSInteger kOrderInternalRow = 0; +static const NSInteger kOrderCollectedPoiTypesRow = 40; +static const NSInteger kOrderOsmUrlRow = 10000; + @interface OAPOIViewController () @end @@ -77,6 +81,14 @@ - (id) initWithPOI:(OAPOI *)poi return self; } +- (void) setObject:(id)object +{ + if ([object isKindOfClass:OAPOI.class]) + { + [self setup:object]; + } +} + - (void) setup:(OAPOI *)poi { self.poi = poi; @@ -309,7 +321,7 @@ - (void)addRemainingEntriesFrom:(NSDictionary *)resultDict to:(NSMutableDictiona } } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { BOOL hasWiki = NO; NSString *preferredLang = [OAUtilities preferredLang]; @@ -645,7 +657,7 @@ - (void) buildRows:(NSMutableArray *)rows textColor:nil isText:YES needLinks:YES - order:0 + order:kOrderInternalRow typeName:@"" isPhoneNumber:NO isUrl:NO]; @@ -781,7 +793,7 @@ - (void) buildRows:(NSMutableArray *)rows textColor:nil isText:NO needLinks:NO - order:40 + order:kOrderCollectedPoiTypesRow typeName:poiCategory.name isPhoneNumber:NO isUrl:NO]; @@ -838,7 +850,7 @@ - (void) buildRows:(NSMutableArray *)rows textColor:[UIColor colorNamed:ACColorNameTextColorActive] isText:YES needLinks:YES - order:10000 + order:kOrderOsmUrlRow typeName:nil isPhoneNumber:NO isUrl:YES]]; diff --git a/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift b/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift new file mode 100644 index 0000000000..e87201e367 --- /dev/null +++ b/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift @@ -0,0 +1,49 @@ +// +// PlaceDetailsViewController.swift +// OsmAnd +// +// Created by Max Kojin on 02/09/25. +// Copyright © 2025 OsmAnd. All rights reserved. +// + +@objcMembers +final class PlaceDetailsViewController: OAPOIViewController { + + private var detailsObject: BaseDetailsObject? + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: "OAPOIViewController", bundle: nibBundleOrNil) + } + + init(poi: OAPOI, detailsObject: BaseDetailsObject) { + super.init(poi: poi) + self.detailsObject = detailsObject + setObject(detailsObject) + } + + override func setObject(_ object: Any!) { + if let detailsObj = object as? BaseDetailsObject { + poi = detailsObj.syntheticAmenity + } else { + super.setObject(object) + } + } + +// override func buildTopRows(_ rows: NSMutableArray!) { +// super.buildTopRows(rows) +// } + + // override if needed + //- (void) buildRowsInternal:(NSMutableArray *)rows + //- (void) buildTopRows:(NSMutableArray *)rows + //- (void) buildWithinRow + //- (void) buildRows:(NSMutableArray *)rows + //- (void) buildRowsPoi:(BOOL)isWiki + //- (void) buildCoordinateRows:(NSMutableArray *)rows + //- (void) addNearbyImagesIfNeeded + //- (void) addMapillaryCardsRowInfoIfNeeded +} diff --git a/Sources/Controllers/TargetMenu/PointEditing/OAGpxWptEditingHandler.mm b/Sources/Controllers/TargetMenu/PointEditing/OAGpxWptEditingHandler.mm index f7e0c6374e..2ff2ed3700 100644 --- a/Sources/Controllers/TargetMenu/PointEditing/OAGpxWptEditingHandler.mm +++ b/Sources/Controllers/TargetMenu/PointEditing/OAGpxWptEditingHandler.mm @@ -73,7 +73,7 @@ - (instancetype)initWithLocation:(CLLocationCoordinate2D)location title:(NSStrin [p setIconNameIconName:_iconName]; [p setBackgroundTypeBackType:DEFAULT_ICON_SHAPE_KEY]; [p setAddressAddress:address]; - NSDictionary *extensions = [poi toTagValue:PRIVATE_PREFIX osmPrefix:OSM_PREFIX_KEY]; + NSDictionary *extensions = [poi toTagValue:AMENITY_PREFIX osmPrefix:OSM_PREFIX_KEY]; [[p getExtensionsToWrite] addEntriesFromDictionary:extensions]; NSString *originName = poi.toStringEn; diff --git a/Sources/Controllers/TargetMenu/Transport/OATransportRouteController.mm b/Sources/Controllers/TargetMenu/Transport/OATransportRouteController.mm index 2df02535eb..7a832c861a 100644 --- a/Sources/Controllers/TargetMenu/Transport/OATransportRouteController.mm +++ b/Sources/Controllers/TargetMenu/Transport/OATransportRouteController.mm @@ -27,6 +27,10 @@ #include #include + +static const NSInteger kOrderPreviousStopsCountRow = -1; + + static OATransportRouteToolbarViewController *toolbarController; @interface OATransportRouteController () @@ -213,7 +217,7 @@ - (int) getPreviousStop return -1; } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { const auto& stops = _transportRoute.route->forwardStops; int currentStop = [_transportRoute getStopIndex]; @@ -224,7 +228,7 @@ - (void) buildRows:(NSMutableArray *)rows startPosition = (currentStop == -1 ? 0 : currentStop); if (currentStop > 0) { - OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:@"button" icon:defaultIcon textPrefix:[NSString stringWithFormat:OALocalizedString(@"route_stops_before"), @(currentStop).stringValue] text:OALocalizedString(@"recording_context_menu_show") textColor:nil isText:YES needLinks:NO order:-1 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo *rowInfo = [[OARowInfo alloc] initWithKey:@"button" icon:defaultIcon textPrefix:[NSString stringWithFormat:OALocalizedString(@"route_stops_before"), @(currentStop).stringValue] text:OALocalizedString(@"recording_context_menu_show") textColor:nil isText:YES needLinks:NO order:kOrderPreviousStopsCountRow typeName:@"" isPhoneNumber:NO isUrl:NO]; rowInfo.delegate = self; [rows addObject:rowInfo]; } diff --git a/Sources/Controllers/TargetMenu/Transport/OATransportStopViewController.mm b/Sources/Controllers/TargetMenu/Transport/OATransportStopViewController.mm index e192bc816c..a806ade6f6 100644 --- a/Sources/Controllers/TargetMenu/Transport/OATransportStopViewController.mm +++ b/Sources/Controllers/TargetMenu/Transport/OATransportStopViewController.mm @@ -93,13 +93,13 @@ - (ETopToolbarType) topToolbarType return ETopToolbarTypeFloating; } -- (void) buildRows:(NSMutableArray *)rows +- (void) buildInternal:(NSMutableArray *)rows { OAPOI *poi = self.transportStop.poi; if (poi) { OAPOIViewController *poiController = [[OAPOIViewController alloc] initWithPOI:poi]; - [poiController buildRows:rows]; + [poiController buildInternal:rows]; } } diff --git a/Sources/Controllers/TargetMenu/Wiki/OAWikiMenuViewController.m b/Sources/Controllers/TargetMenu/Wiki/OAWikiMenuViewController.m index adceb53b50..111d7b6947 100644 --- a/Sources/Controllers/TargetMenu/Wiki/OAWikiMenuViewController.m +++ b/Sources/Controllers/TargetMenu/Wiki/OAWikiMenuViewController.m @@ -10,6 +10,9 @@ #import "OAPOI.h" #import "Localization.h" +static const NSInteger kOrderContentRow = 1; + + @interface OAWikiMenuViewController () @end @@ -25,7 +28,7 @@ - (id)initWithPOI:(OAPOI *)poi content:(NSString *)content if (self) { _content = content; - OARowInfo* contentRow = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:nil text:content textColor:nil isText:YES needLinks:NO order:1 typeName:@"" isPhoneNumber:NO isUrl:NO]; + OARowInfo* contentRow = [[OARowInfo alloc] initWithKey:nil icon:[OATargetInfoViewController getIcon:@"ic_description.png"] textPrefix:nil text:content textColor:nil isText:YES needLinks:NO order:kOrderContentRow typeName:@"" isPhoneNumber:NO isUrl:NO]; contentRow.isHtml = YES; contentRow.delegate = self; if (contentRow.isText && !NSStringIsEmpty(contentRow.text)) diff --git a/Sources/Data/OATargetPoint.h b/Sources/Data/OATargetPoint.h index c94ebe7b40..777179e3b5 100644 --- a/Sources/Data/OATargetPoint.h +++ b/Sources/Data/OATargetPoint.h @@ -9,7 +9,7 @@ #import #import -@class OAPointDescription; +@class OAPointDescription, BaseDetailsObject; typedef NS_ENUM(NSInteger, OATargetPointType) { @@ -74,6 +74,7 @@ typedef NS_ENUM(NSInteger, OATargetPointType) @property (nonatomic) NSDictionary *localizedContent; @property (nonatomic) id targetObj; +@property (nonatomic) BaseDetailsObject *detailsObj; @property (nonatomic) BOOL toolbarNeeded; @property (nonatomic) NSInteger segmentIndex; @@ -87,10 +88,11 @@ typedef NS_ENUM(NSInteger, OATargetPointType) @property (nonatomic, readonly) OAPointDescription *pointDescription; @property (nonatomic) int symbolId; -@property (nonatomic) unsigned long long obfId; +@property (nonatomic) uint64_t obfId; @property (nonatomic) NSInteger sortIndex; @property (nonatomic) NSString* symbolGroupId; - (void)initAdderssIfNeeded; +- (void)initDetailsObjectIfNeeded:(id)object; @end diff --git a/Sources/Data/OATargetPoint.m b/Sources/Data/OATargetPoint.m index eaf6a80cf6..3a768380d9 100644 --- a/Sources/Data/OATargetPoint.m +++ b/Sources/Data/OATargetPoint.m @@ -12,6 +12,8 @@ #import "OAReverseGeocoder.h" #import "Localization.h" #import "OAPOI.h" +#import "OARenderedObject.h" +#import "OAAmenitySearcher.h" @implementation OATargetPoint { @@ -52,6 +54,20 @@ - (void)initAdderssIfNeeded _addressFound = isAddressFound; } +- (void) initDetailsObjectIfNeeded:(id)object +{ + // Android has this code here: MapContextMenu.init(...) + + if (![object isKindOfClass:OARenderedObject.class]) + { + BaseDetailsObject *detailsObject = [OAAmenitySearcher.sharedInstance searchDetailedObject:object]; + if (detailsObject) + { + _detailsObj = detailsObject; + } + } +} + - (OAPointDescription *) pointDescription { if (!_pd) diff --git a/Sources/GPX/OAGPXDocumentPrimitives.h b/Sources/GPX/OAGPXDocumentPrimitives.h index 284141c613..2e20fbe907 100644 --- a/Sources/GPX/OAGPXDocumentPrimitives.h +++ b/Sources/GPX/OAGPXDocumentPrimitives.h @@ -11,22 +11,22 @@ #import "OALocationPoint.h" #import "OsmAndSharedWrapper.h" -#define ICON_NAME_EXTENSION_KEY @"icon" -#define BACKGROUND_TYPE_EXTENSION_KEY @"background" -#define COLOR_NAME_EXTENSION_KEY @"color" -#define ADDRESS_EXTENSION_KEY @"address" -#define CALENDAR_EXTENSION @"calendar_event" -#define PICKUP_DATE @"pickup_date" -#define VISITED_TIME_EXTENSION @"visited_date" -#define CREATION_TIME_EXTENSION @"creation_date" -#define PICKUP_DATE_EXTENSION @"pickup_date" -#define DEFAULT_ICON_NAME_KEY @"special_star" -#define DEFAULT_ICON_SHAPE_KEY @"circle" -#define PROFILE_TYPE_EXTENSION_KEY @"profile" +static NSString * ICON_NAME_EXTENSION_KEY = @"icon"; +static NSString * BACKGROUND_TYPE_EXTENSION_KEY = @"background"; +static NSString * COLOR_NAME_EXTENSION_KEY = @"color"; +static NSString * ADDRESS_EXTENSION_KEY = @"address"; +static NSString * CALENDAR_EXTENSION = @"calendar_event"; +static NSString * PICKUP_DATE = @"pickup_date"; +static NSString * VISITED_TIME_EXTENSION = @"visited_date"; +static NSString * CREATION_TIME_EXTENSION = @"creation_date"; +static NSString * PICKUP_DATE_EXTENSION = @"pickup_date"; +static NSString * DEFAULT_ICON_NAME_KEY = @"special_star"; +static NSString * DEFAULT_ICON_SHAPE_KEY = @"circle"; +static NSString * PROFILE_TYPE_EXTENSION_KEY = @"profile"; -#define PRIVATE_PREFIX @"amenity_" -#define AMENITY_ORIGIN_EXTENSION_KEY @"amenity_origin" -#define OSM_PREFIX_KEY @"osm_tag_" +static NSString * AMENITY_PREFIX = @"amenity_"; +static NSString * AMENITY_ORIGIN_EXTENSION_KEY = @"amenity_origin"; +static NSString * OSM_PREFIX_KEY = @"osm_tag_"; static NSString * const kGapProfileTypeKey = @"gap"; static NSString * const kTrkptIndexExtension = @"trkpt_idx"; diff --git a/Sources/Helpers/OAAmenitySearcher.h b/Sources/Helpers/OAAmenitySearcher.h index 7b9122814c..b1e127ac4c 100644 --- a/Sources/Helpers/OAAmenitySearcher.h +++ b/Sources/Helpers/OAAmenitySearcher.h @@ -33,7 +33,8 @@ NS_ASSUME_NONNULL_BEGIN + (OAAmenitySearcher *) sharedInstance; -- (nullable BaseDetailsObject *)searchDetailedObject:(OAAmenitySearcherRequest *)request; +- (nullable BaseDetailsObject *)searchDetailedObject:(id)object; +- (nullable BaseDetailsObject *)searchDetailedObjectWithRequest:(OAAmenitySearcherRequest *)request; - (BOOL) breakSearch; @@ -43,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSArray *) findPOIsByFilter:(OASearchPoiTypeFilter *)filter topLatitude:(double)topLatitude leftLongitude:(double)leftLongitude bottomLatitude:(double)bottomLatitude rightLongitude:(double)rightLongitude matcher:(OAResultMatcher *)matcher; + (NSArray *) findPOIsByName:(NSString *)query topLatitude:(double)topLatitude leftLongitude:(double)leftLongitude bottomLatitude:(double)bottomLatitude rightLongitude:(double)rightLongitude matcher:(OAResultMatcher *)matcher; + (NSArray *) searchPOIsOnThePath:(NSArray *)locations radius:(double)radius filter:(OASearchPoiTypeFilter *)filter matcher:(OAResultMatcher *)matcher; -+ (OAPOI *) findPOIByOsmId:(long long)osmId lat:(double)lat lon:(double)lon; ++ (OAPOI *) findPOIByOsmId:(uint64_t)osmId lat:(double)lat lon:(double)lon; + (OAPOI *) findPOIByName:(NSString *)name lat:(double)lat lon:(double)lon; + (OAPOI *) findPOIByOriginName:(NSString *)originName lat:(double)lat lon:(double)lon; + (NSArray *) findPOI:(OASearchPoiTypeFilter *)searchFilter additionalFilter:(OATopIndexFilter *)additionalFilter lat:(double)lat lon:(double)lon radius:(int)radius includeTravel:(BOOL)includeTravel matcher:(OAResultMatcher *)matcher publish:(BOOL(^)(OAPOI *poi))publish; diff --git a/Sources/Helpers/OAAmenitySearcher.mm b/Sources/Helpers/OAAmenitySearcher.mm index 4428306984..f4ff583b21 100644 --- a/Sources/Helpers/OAAmenitySearcher.mm +++ b/Sources/Helpers/OAAmenitySearcher.mm @@ -92,10 +92,9 @@ - (instancetype)initWithMapObject:(OAMapObject *)mapObject { _latLon = renderedObject.labelLatLon; } - if (!NSDictionaryIsEmpty(renderedObject.localizedNames)) - { - [_names addObjectsFromArray:(NSArray *)renderedObject.localizedNames.allValues]; - } + + _names = [renderedObject getOriginalNames]; + NSString *value = renderedObject.tags[WIKIDATA_TAG]; if (value) { @@ -198,7 +197,37 @@ - (BOOL) breakSearch return [OAAmenitySearcher findPOI:[OASearchPoiTypeFilter acceptAllPoiTypeFilter] additionalFilter:nil lat:searchLatLon.coordinate.latitude lon:searchLatLon.coordinate.longitude radius:radius includeTravel:includeTravel matcher:nil publish:nil]; } -- (nullable BaseDetailsObject *)searchDetailedObject:(OAAmenitySearcherRequest *)request +- (nullable BaseDetailsObject *)searchDetailedObject:(id)object +{ + OAAmenitySearcherRequest *request; + if ([object isKindOfClass:OAMapObject.class]) + { + request = [[OAAmenitySearcherRequest alloc] initWithMapObject:object]; + } + else if ([object isKindOfClass:BaseDetailsObject.class]) + { + BaseDetailsObject *detailsObject = object; + if ([detailsObject isObjectFull]) + { + [self completeGeometry:detailsObject object:detailsObject.objects[0]]; + return detailsObject; + } + if (!NSArrayIsEmpty(detailsObject.objects)) + { + return detailsObject = [self searchDetailedObject:detailsObject.objects[0]]; + } + } + BaseDetailsObject *detailsObject; + if (request) + { + detailsObject = [self searchDetailedObjectWithRequest:request]; + } + + [self completeGeometry:detailsObject object:object]; + return detailsObject; +} + +- (nullable BaseDetailsObject *)searchDetailedObjectWithRequest:(OAAmenitySearcherRequest *)request { if (request.latLon == nil) { @@ -254,7 +283,7 @@ - (nullable BaseDetailsObject *)searchDetailedObject:(OAAmenitySearcherRequest * return nil; } -- (NSArray *)filterByOsmIdOrWikidata:(NSArray *)amenities osmId:(int64_t)osmId point:(CLLocation *)point wikidata:(nullable NSString *)wikidata +- (NSMutableArray *)filterByOsmIdOrWikidata:(NSArray *)amenities osmId:(int64_t)osmId point:(CLLocation *)point wikidata:(nullable NSString *)wikidata { NSMutableArray *result = [NSMutableArray array]; double minDist = AMENITY_SEARCH_RADIUS_FOR_RELATION * 4; @@ -383,6 +412,59 @@ - (BOOL)namesMatcher:(OAPOI *)amenity matchList:(NSArray *)matchList return NO; } +- (void) completeGeometry:(BaseDetailsObject *)detailsObject object:(id)object +{ + if (!detailsObject) + return; + + NSMutableArray *xx; + NSMutableArray *yy; + + if ([object isKindOfClass:OAPOI.class]) + { + OAPOI *amenity = object; + xx = amenity.x; + yy = amenity.y; + } + if ([object isKindOfClass:OARenderedObject.class]) + { + OARenderedObject *renderedObject = object; + xx = renderedObject.x; + yy = renderedObject.y; + } + if ([object isKindOfClass:BaseDetailsObject.class]) + { + BaseDetailsObject *base = object; + xx = [base syntheticAmenity].x; + yy = [base syntheticAmenity].y; + } + + if (!NSArrayIsEmpty(xx) && !NSArrayIsEmpty(yy)) + { + [detailsObject setX:xx]; + [detailsObject setY:yy]; + } + else + { + + //TODO: implement + +// List dataObjects = searchBinaryMapDataForAmenity(detailsObject.getSyntheticAmenity(), 1); +// for (BinaryMapDataObject dataObject : dataObjects) { +// if (copyCoordinates(detailsObject, dataObject)) { +// break; +// } +// } + } +} + +- (void) searchBinaryMapDataForAmenity:(OAPOI *)amenity limit:(int)limit +{ + //TODO: implement + + +} + // MARK: OAPOIHeler poi find methods // here are all existed methods exrtracted as is. @@ -810,7 +892,7 @@ + (OAPOI *)findPOIByName:(NSString *)name lat:(double)lat lon:(double)lon return nil; } -+ (OAPOI *) findPOIByOsmId:(long long)osmId lat:(double)lat lon:(double)lon ++ (OAPOI *) findPOIByOsmId:(uint64_t)osmId lat:(double)lat lon:(double)lon { OsmAndAppInstance app = [OsmAndApp instance]; const auto& obfsCollection = app.resourcesManager->obfsCollection; @@ -1123,9 +1205,6 @@ + (OAPOI *) findPOIByOriginName:(NSString *)originName lat:(double)lat lon:(doub + (NSArray *) findPOI:(OASearchPoiTypeFilter *)searchFilter additionalFilter:(OATopIndexFilter *)additionalFilter lat:(double)lat lon:(double)lon radius:(int)radius includeTravel:(BOOL)includeTravel matcher:(OAResultMatcher *)matcher publish:(BOOL(^)(OAPOI *poi))publish { - CLLocation *currentLocation = OsmAndApp.instance.locationServices.lastKnownLocation; - OsmAnd::PointI currentLocation31 = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude)); - OsmAnd::PointI point31 = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(lat, lon)); OsmAnd::AreaI bbox31 = (OsmAnd::AreaI)OsmAnd::Utilities::boundingBox31FromAreaInMeters(radius, point31); return [self findPOI:searchFilter additionalFilter:additionalFilter bbox31:bbox31 currentLocation:point31 includeTravel:includeTravel matcher:matcher publish:publish]; @@ -1179,13 +1258,16 @@ + (OAPOI *) findPOIByOriginName:(NSString *)originName lat:(double)lat lon:(doub [&filter, &foundAmenities, ¤tLocation, &processedPoi, &publish, &done](const OsmAnd::ISearch::Criteria& criteria, const OsmAnd::ISearch::IResultEntry& resultEntry) { const auto &am = ((OsmAnd::AmenitiesByNameSearch::ResultEntry&)resultEntry).amenity; - + + uint64_t obfId = am->id.id; + OAPOIType *type = [OAAmenitySearcher parsePOITypeByAmenity:am]; BOOL accept = [filter accept:type.category subcategory:type.name]; - if (![processedPoi containsObject:@(am->id.id)] && accept) + + if (![processedPoi containsObject:@(obfId)] && accept) { - [processedPoi addObject:@(am->id.id)]; + [processedPoi addObject:@(obfId)]; OAPOI *poi = [OAAmenitySearcher parsePOI:resultEntry withValues:YES withContent:YES]; poi.distanceMeters = OsmAnd::Utilities::squareDistance31(currentLocation, am->position31); diff --git a/Sources/Map/ObfConstants.swift b/Sources/Map/ObfConstants.swift index 3ccdebf3a7..d6a52ce88d 100644 --- a/Sources/Map/ObfConstants.swift +++ b/Sources/Map/ObfConstants.swift @@ -11,15 +11,15 @@ import Foundation @objcMembers final class ObfConstants: NSObject { - static let SHIFT_MULTIPOLYGON_IDS: Int64 = 43 - static let SHIFT_NON_SPLIT_EXISTING_IDS: Int64 = 41 - static let SHIFT_PROPAGATED_NODE_IDS: Int64 = 50 - static let SHIFT_PROPAGATED_NODES_BITS: Int64 = 11 - static let MAX_ID_PROPAGATED_NODES: Int64 = (1 << SHIFT_PROPAGATED_NODES_BITS) - 1 // 2047 - static let RELATION_BIT: Int64 = 1 << (SHIFT_MULTIPOLYGON_IDS - 1) // 1L << 42 - static let PROPAGATE_NODE_BIT: Int64 = 1 << (SHIFT_PROPAGATED_NODE_IDS - 1) // 1L << 41 - static let SPLIT_BIT: Int64 = 1 << (SHIFT_NON_SPLIT_EXISTING_IDS - 1) // 1L << 40 - static let DUPLICATE_SPLIT: Int64 = 5 // According IndexPoiCreator DUPLICATE_SPLIT + static let SHIFT_MULTIPOLYGON_IDS: UInt64 = 43 + static let SHIFT_NON_SPLIT_EXISTING_IDS: UInt64 = 41 + static let SHIFT_PROPAGATED_NODE_IDS: UInt64 = 50 + static let SHIFT_PROPAGATED_NODES_BITS: UInt64 = 11 + static let MAX_ID_PROPAGATED_NODES: UInt64 = (1 << SHIFT_PROPAGATED_NODES_BITS) - 1 // 2047 + static let RELATION_BIT: UInt64 = 1 << (SHIFT_MULTIPOLYGON_IDS - 1) // 1L << 42 + static let PROPAGATE_NODE_BIT: UInt64 = 1 << (SHIFT_PROPAGATED_NODE_IDS - 1) // 1L << 41 + static let SPLIT_BIT: UInt64 = 1 << (SHIFT_NON_SPLIT_EXISTING_IDS - 1) // 1L << 40 + static let DUPLICATE_SPLIT: UInt64 = 5 // According IndexPoiCreator DUPLICATE_SPLIT static private let SHIFT_ID = 6 static private let AMENITY_ID_RIGHT_SHIFT = 1 @@ -35,7 +35,7 @@ final class ObfConstants: NSObject { return "https://www.openstreetmap.org/\(type)/\(osmId)" } - static func createMapObjectIdFromOsmId(_ osmId: Int64, type: String?) -> Int64 { + static func createMapObjectIdFromOsmId(_ osmId: UInt64, type: String?) -> UInt64 { guard let type else { return osmId } switch type { @@ -50,10 +50,10 @@ final class ObfConstants: NSObject { } } - static func getOsmObjectId(_ object: OAMapObject) -> Int64 { - var originalId: Int64 = -1 + static func getOsmObjectId(_ object: OAMapObject) -> UInt64 { + var originalId: UInt64 = 0 var obfId = object.obfId - if obfId != -1 { + if obfId > 0 { if object is OARenderedObject { obfId >>= 1 } @@ -74,7 +74,7 @@ final class ObfConstants: NSObject { static func getOsmEntityType(_ object: OAMapObject) -> String? { if isOsmUrlAvailable(object) { - let obfId = object.obfId + let obfId = UInt64(object.obfId) let originalId = obfId >> 1 if object is OARenderedObject && isIdFromPropagatedNode(originalId) { return WAY @@ -96,26 +96,26 @@ final class ObfConstants: NSObject { return object.obfId > 0 } - static func getOsmId(_ obfId: Int64) -> Int64 { + static func getOsmId(_ obfId: UInt64) -> UInt64 { // According methods assignIdForMultipolygon and genId in IndexPoiCreator let clearBits = RELATION_BIT | SPLIT_BIT let midifiedId = isShiftedID(obfId) ? (obfId & ~clearBits) >> DUPLICATE_SPLIT : obfId return midifiedId >> Self.SHIFT_ID } - static func isShiftedID(_ obfId: Int64) -> Bool { + static func isShiftedID(_ obfId: UInt64) -> Bool { isIdFromRelation(obfId) || isIdFromSplit(obfId) } - static func isIdFromRelation(_ obfId: Int64) -> Bool { + static func isIdFromRelation(_ obfId: UInt64) -> Bool { obfId > 0 && (obfId & Self.RELATION_BIT) == Self.RELATION_BIT } - static func isIdFromPropagatedNode(_ obfId: Int64) -> Bool { + static func isIdFromPropagatedNode(_ obfId: UInt64) -> Bool { obfId > 0 && (obfId & Self.PROPAGATE_NODE_BIT) == Self.PROPAGATE_NODE_BIT } - static func isIdFromSplit(_ obfId: Int64) -> Bool { + static func isIdFromSplit(_ obfId: UInt64) -> Bool { obfId > 0 && (obfId & Self.SPLIT_BIT) == Self.SPLIT_BIT } diff --git a/Sources/Models/OAFavoriteItem.mm b/Sources/Models/OAFavoriteItem.mm index cd3babdfa8..ce151678e6 100644 --- a/Sources/Models/OAFavoriteItem.mm +++ b/Sources/Models/OAFavoriteItem.mm @@ -528,7 +528,7 @@ - (OAPOI *) getAmenity for (const auto& extension : OsmAnd::rangeOf(extensionsToRead)) extensions[extension.key().toNSString()] = extension.value().toNSString(); - _amenity = [OAPOI fromTagValue:extensions privatePrefix:PRIVATE_PREFIX osmPrefix:OSM_PREFIX_KEY]; + _amenity = [OAPOI fromTagValue:extensions privatePrefix:AMENITY_PREFIX osmPrefix:OSM_PREFIX_KEY]; return _amenity; } return nil; @@ -538,7 +538,7 @@ - (void) setAmenity:(OAPOI *)amenity { if (amenity) { - NSDictionary *extensions = [amenity toTagValue:PRIVATE_PREFIX osmPrefix:OSM_PREFIX_KEY]; + NSDictionary *extensions = [amenity toTagValue:AMENITY_PREFIX osmPrefix:OSM_PREFIX_KEY]; [extensions enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull value, BOOL * _Nonnull stop) { self.favorite->setExtension(QString::fromNSString(key), QString::fromNSString(value)); }]; diff --git a/Sources/OsmAnd Maps-Bridging-Header.h b/Sources/OsmAnd Maps-Bridging-Header.h index 82e0b60d00..e0e77fd1b8 100644 --- a/Sources/OsmAnd Maps-Bridging-Header.h +++ b/Sources/OsmAnd Maps-Bridging-Header.h @@ -92,6 +92,7 @@ #import "OAContextMenuProvider.h" #import "EOAEntityType.h" #import "OAAmenitySearcher.h" +#import "OAGPXDocumentPrimitives.h" // Widgets #import "OAMapWidgetRegistry.h" diff --git a/Sources/OsmEdit/OAOpenStreetMapLocalUtil.mm b/Sources/OsmEdit/OAOpenStreetMapLocalUtil.mm index 9ba6661255..5dc3aae9b2 100644 --- a/Sources/OsmEdit/OAOpenStreetMapLocalUtil.mm +++ b/Sources/OsmEdit/OAOpenStreetMapLocalUtil.mm @@ -65,7 +65,7 @@ - (OAEntityInfo *)getEntityInfo:(long long)identifier - (OAEntity *)loadEntity:(OATargetPoint *)targetPoint { OAPOIHelper *poiHelper = [OAPOIHelper sharedInstance]; - long long objectId = targetPoint.obfId; + uint64_t objectId = targetPoint.obfId; BOOL isTransportStop = targetPoint.type == OATargetTransportStop; if (isTransportStop) objectId = ((OATransportStop *)targetPoint.targetObj).poi.obfId; @@ -78,7 +78,7 @@ - (OAEntity *)loadEntity:(OATargetPoint *)targetPoint OAPOIType *poiType = poi.type; BOOL isAmenity = poiType && ![poiType isKindOfClass:[OAPOILocationType class]]; - long long entityId = objectId >> (isAmenity ? AMENITY_ID_RIGHT_SHIFT : NON_AMENITY_ID_RIGHT_SHIFT); + uint64_t entityId = objectId >> (isAmenity ? AMENITY_ID_RIGHT_SHIFT : NON_AMENITY_ID_RIGHT_SHIFT); BOOL isWay = objectId % 2 == WAY_MODULO_REMAINDER; // check if mapObject is a way OAEntity *entity; diff --git a/Sources/OsmEdit/OAOpenStreetMapRemoteUtil.mm b/Sources/OsmEdit/OAOpenStreetMapRemoteUtil.mm index 4a52cc4fd5..6a327995cb 100644 --- a/Sources/OsmEdit/OAOpenStreetMapRemoteUtil.mm +++ b/Sources/OsmEdit/OAOpenStreetMapRemoteUtil.mm @@ -369,7 +369,7 @@ - (OAEntityInfo *)getEntityInfo:(long long)identifier { - (OAEntity *)loadEntity:(OATargetPoint *)targetPoint { - unsigned long long objectId = targetPoint.obfId; + uint64_t objectId = targetPoint.obfId; BOOL isTransportStop = targetPoint.type == OATargetTransportStop; if (isTransportStop) objectId = ((OATransportStop *)targetPoint.targetObj).poi.obfId; diff --git a/Sources/POI/OAMapObject.h b/Sources/POI/OAMapObject.h index aa46c0ccbc..3bab3df36c 100644 --- a/Sources/POI/OAMapObject.h +++ b/Sources/POI/OAMapObject.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OAMapObject : NSObject -@property (nonatomic) long long obfId; +@property (nonatomic) uint64_t obfId; @property (nonatomic, nullable) NSString *name; @property (nonatomic) NSString *enName; diff --git a/Sources/POI/OAPOI.h b/Sources/POI/OAPOI.h index 78ac587d68..c58ccbf9c6 100644 --- a/Sources/POI/OAPOI.h +++ b/Sources/POI/OAPOI.h @@ -10,6 +10,8 @@ #import "OrderedDictionary.h" #import "OAMapObject.h" +extern NSString * const POI_NAME; + extern NSString * const URL_TAG; extern NSString * const WEBSITE_TAG; extern NSString * const PHONE_TAG; @@ -54,6 +56,7 @@ extern NSString * const ROUTE_TRACK_POINT; extern NSString * const ROUTE_BBOX_RADIUS; extern NSString * const ROUTE_MEMBERS_IDS; extern NSString * const TRAVEL_EVO_TAG; +extern NSString * const COLLAPSABLE_PREFIX; static int DEFAULT_ELO = 900; @@ -154,7 +157,7 @@ static int DEFAULT_ELO = 900; - (void) setXYPoints:(OARenderedObject *)renderedObject; -- (int64_t) getOsmId; +- (uint64_t) getOsmId; - (BOOL) strictEquals:(id)object; diff --git a/Sources/POI/OAPOI.mm b/Sources/POI/OAPOI.mm index 683a53ee99..f1695e310d 100644 --- a/Sources/POI/OAPOI.mm +++ b/Sources/POI/OAPOI.mm @@ -76,9 +76,9 @@ BACKGROUND_TYPE_EXTENSION_KEY, PROFILE_TYPE_EXTENSION_KEY, ADDRESS_EXTENSION_KEY, - [NSString stringWithFormat:@"%@%@", PRIVATE_PREFIX, AMENITY_NAME], - [NSString stringWithFormat:@"%@%@", PRIVATE_PREFIX, TYPE], - [NSString stringWithFormat:@"%@%@", PRIVATE_PREFIX, SUBTYPE] + [NSString stringWithFormat:@"%@%@", AMENITY_PREFIX, AMENITY_NAME], + [NSString stringWithFormat:@"%@%@", AMENITY_PREFIX, TYPE], + [NSString stringWithFormat:@"%@%@", AMENITY_PREFIX, SUBTYPE] ]; @implementation OAPOIRoutePoint @@ -829,11 +829,11 @@ - (void) setXYPoints:(OARenderedObject *)renderedObject self.y = renderedObject.y; } -- (int64_t) getOsmId +- (uint64_t) getOsmId { - int64_t _osmId = self.obfId; - if (_osmId == -1) - return -1; + uint64_t _osmId = self.obfId; + if (_osmId <= 0) + return 0; if ([ObfConstants isShiftedID:_osmId]) return [ObfConstants getOsmId:_osmId]; diff --git a/Sources/POI/OARenderedObject.h b/Sources/POI/OARenderedObject.h index e36383f656..226a1a87f9 100644 --- a/Sources/POI/OARenderedObject.h +++ b/Sources/POI/OARenderedObject.h @@ -30,6 +30,8 @@ @property (nonatomic) BOOL isPolygon; +- (NSMutableArray *) getOriginalNames; + - (void) setBBox:(int)left top:(int)top right:(int)right bottom:(int)bottom; - (BOOL) isText; - (long) estimatedArea; diff --git a/Sources/POI/OARenderedObject.mm b/Sources/POI/OARenderedObject.mm index 193030431b..750b090a2a 100644 --- a/Sources/POI/OARenderedObject.mm +++ b/Sources/POI/OARenderedObject.mm @@ -101,4 +101,20 @@ - (long) estimatedArea return abs(_bboxRight - _bboxLeft) * abs(_bboxTop - _bboxBottom); } +- (NSMutableArray *) getOriginalNames +{ + NSMutableArray *names = [NSMutableArray new]; + if (!NSStringIsEmpty(self.name)) + [names addObject:self.name]; + + for (NSString *key in self.localizedNames.allKeys) + { + NSString *value = self.localizedNames[key]; + if (!NSStringIsEmpty(value)) + [names addObject:value]; + } + + return names; +} + @end diff --git a/Sources/Views/OATargetMultiView.mm b/Sources/Views/OATargetMultiView.mm index 8a2f1f771d..70c459704a 100644 --- a/Sources/Views/OATargetMultiView.mm +++ b/Sources/Views/OATargetMultiView.mm @@ -126,7 +126,7 @@ -(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath { SelectedMapObject *selectedObject = _selectedMapObjects[indexPath.row]; OAContextMenuLayer *contextLayer = [OARootViewController instance].mapPanel.mapViewController.mapLayers.contextMenuLayer; - [contextLayer showContextMenu:_touchPoint object:selectedObject]; + [contextLayer showContextMenuFor:selectedObject latLon:_touchPoint]; } else { diff --git a/Sources/Views/OATargetPointView.h b/Sources/Views/OATargetPointView.h index 39c31065c6..a54d85838a 100644 --- a/Sources/Views/OATargetPointView.h +++ b/Sources/Views/OATargetPointView.h @@ -28,6 +28,8 @@ @property (nonatomic, assign) OATargetPointType activeTargetType; +@property (nonatomic) id selectedObject; + @property (nonatomic, readonly) BOOL showFull; @property (nonatomic, readonly) BOOL showFullScreen; @property (nonatomic) BOOL skipOpenRouteSettings;