diff --git a/OsmAnd.xcodeproj/project.pbxproj b/OsmAnd.xcodeproj/project.pbxproj index c30c76fa18..10a2de288a 100644 --- a/OsmAnd.xcodeproj/project.pbxproj +++ b/OsmAnd.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ 15CD7EA725B1C1A700BCB36A /* ic_small_time_start@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 15CD7EA425B1C1A700BCB36A /* ic_small_time_start@2x.png */; }; 15CD7EAD25B1C1B900BCB36A /* ic_small_waypoints@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 15CD7EAA25B1C1B700BCB36A /* ic_small_waypoints@3x.png */; }; 15CD7EAF25B1C1B900BCB36A /* ic_small_waypoints@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 15CD7EAC25B1C1B800BCB36A /* ic_small_waypoints@2x.png */; }; + 228EDAF170A7A81DF3A85783 /* UIImage+RouteActivityIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BD9FA9C2F7AD66558B5B407 /* UIImage+RouteActivityIcon.swift */; }; 2702BC612EF98AD900A545A2 /* TopBottomValuesSliderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2702BC602EF98AD900A545A2 /* TopBottomValuesSliderTableViewCell.xib */; }; 2702BC622EF98AD900A545A2 /* TopBottomValuesSliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2702BC5F2EF98AD900A545A2 /* TopBottomValuesSliderTableViewCell.swift */; }; 2709E3B22EFEE09B0036E686 /* BaseAppearanceIconCollectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2709E3B12EFEE09B0036E686 /* BaseAppearanceIconCollectionHandler.swift */; }; @@ -385,7 +386,6 @@ 32210DB4298929B0005D16CB /* ic_custom_map_pin_outlined@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32210DB0298929B0005D16CB /* ic_custom_map_pin_outlined@3x.png */; }; 32210DB729893C17005D16CB /* OAOsmUploadGPXViewConroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 32210DB629893C17005D16CB /* OAOsmUploadGPXViewConroller.m */; }; 322122152D5F6F3200A3AC9A /* activities.json in Resources */ = {isa = PBXBuildFile; fileRef = 322122142D5F6F3200A3AC9A /* activities.json */; }; - 3226C1A72D3E66FF008F9905 /* RenderedObjectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3226C1A62D3E66DB008F9905 /* RenderedObjectViewController.swift */; }; 322AF8FC2A0CDA7D00D2FF37 /* OAPluginDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 322AF8FB2A0CD68500D2FF37 /* OAPluginDetailsViewController.xib */; }; 322B53922C78DBF8006B48B0 /* IconCollectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322B53912C78DBF8006B48B0 /* IconCollectionHandler.swift */; }; 322B53952C78DD14006B48B0 /* OAIconsPaletteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 322B53942C78DD14006B48B0 /* OAIconsPaletteCell.m */; }; @@ -668,7 +668,6 @@ 460679FB27708A980076740B /* OARouteLineAppearanceHudViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 460679FA27708A980076740B /* OARouteLineAppearanceHudViewController.xib */; }; 460767122A49F86400AF63F2 /* WikiAlgorithms.swift in Sources */ = {isa = PBXBuildFile; fileRef = 460767112A49F86400AF63F2 /* WikiAlgorithms.swift */; }; 4609FAA22C5158F100B57F06 /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4609FAA12C5158F100B57F06 /* UIImageView+Extension.swift */; }; - 228EDAF170A7A81DF3A85783 /* UIImage+RouteActivityIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BD9FA9C2F7AD66558B5B407 /* UIImage+RouteActivityIcon.swift */; }; 460C1E702BFC9B490090D67B /* QuickActionIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 460C1E6F2BFC9B490090D67B /* QuickActionIds.swift */; }; 460E9AC12B71111000411854 /* CloudTrashItemMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 460E9AC02B71111000411854 /* CloudTrashItemMenuViewController.swift */; }; 4611DF3A28569C5000885FD1 /* OASubscriptionBannerCardView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4611DF3928569C5000885FD1 /* OASubscriptionBannerCardView.xib */; }; @@ -4042,7 +4041,6 @@ 32210DB529893C05005D16CB /* OAOsmUploadGPXViewConroller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAOsmUploadGPXViewConroller.h; sourceTree = ""; }; 32210DB629893C17005D16CB /* OAOsmUploadGPXViewConroller.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OAOsmUploadGPXViewConroller.m; sourceTree = ""; }; 322122142D5F6F3200A3AC9A /* activities.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = activities.json; path = ../resources/poi/activities.json; sourceTree = SOURCE_ROOT; }; - 3226C1A62D3E66DB008F9905 /* RenderedObjectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderedObjectViewController.swift; sourceTree = ""; }; 3226C1A82D3E67D5008F9905 /* OARenderedObject+cpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OARenderedObject+cpp.h"; sourceTree = ""; }; 322AF8FB2A0CD68500D2FF37 /* OAPluginDetailsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OAPluginDetailsViewController.xib; sourceTree = ""; }; 322B53912C78DBF8006B48B0 /* IconCollectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconCollectionHandler.swift; sourceTree = ""; }; @@ -4426,7 +4424,6 @@ 460679FA27708A980076740B /* OARouteLineAppearanceHudViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OARouteLineAppearanceHudViewController.xib; sourceTree = ""; }; 460767112A49F86400AF63F2 /* WikiAlgorithms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WikiAlgorithms.swift; sourceTree = ""; }; 4609FAA12C5158F100B57F06 /* UIImageView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extension.swift"; sourceTree = ""; }; - 8BD9FA9C2F7AD66558B5B407 /* UIImage+RouteActivityIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+RouteActivityIcon.swift"; sourceTree = ""; }; 460C1E6F2BFC9B490090D67B /* QuickActionIds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickActionIds.swift; sourceTree = ""; }; 460E9AC02B71111000411854 /* CloudTrashItemMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTrashItemMenuViewController.swift; sourceTree = ""; }; 4611DF3928569C5000885FD1 /* OASubscriptionBannerCardView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OASubscriptionBannerCardView.xib; sourceTree = ""; }; @@ -5203,6 +5200,7 @@ 8AE943B127A28BE900961319 /* OAWeatherRasterLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAWeatherRasterLayer.h; sourceTree = ""; }; 8AE943B227A28BE900961319 /* OAWeatherRasterLayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OAWeatherRasterLayer.mm; sourceTree = ""; }; 8AF348762779D88100A64824 /* libproj_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libproj_static.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 8BD9FA9C2F7AD66558B5B407 /* UIImage+RouteActivityIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+RouteActivityIcon.swift"; sourceTree = ""; }; 8EBE587627297A310075B834 /* test-resources */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "test-resources"; path = "../resources/test-resources"; sourceTree = ""; }; 972189E629DC343600AB0D92 /* map_action_train@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "map_action_train@3x.png"; sourceTree = ""; }; 972189E829DC343600AB0D92 /* map_action_train@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "map_action_train@2x.png"; sourceTree = ""; }; @@ -13274,7 +13272,6 @@ DA5A7E5226C563A400F274C7 /* OAPOIViewController.h */, DA5A7E5326C563A400F274C7 /* OAPOIViewController.mm */, 324F6C642ED5E90F00C76A85 /* PlaceDetailsViewController.swift */, - 3226C1A62D3E66DB008F9905 /* RenderedObjectViewController.swift */, FAF033292F85556E006F1377 /* AmenityExtensionsHelper.swift */, ); path = POI; @@ -17804,7 +17801,6 @@ DA5A83A826C563A800F274C7 /* OAOsmNotesOnlineTargetViewController.mm in Sources */, DA5A839426C563A800F274C7 /* OAOutdatedResourcesViewController.mm in Sources */, DA5A832126C563A800F274C7 /* OAShapesTableViewCell.m in Sources */, - 3226C1A72D3E66FF008F9905 /* RenderedObjectViewController.swift in Sources */, DA5A836826C563A800F274C7 /* OARouteAvoidTransportSettingsViewController.mm in Sources */, DA5A836626C563A800F274C7 /* OARouteStatistics.m in Sources */, 8A90CEDB27A7C2310001C84D /* OAMapSettingsWeatherScreen.mm in Sources */, diff --git a/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm b/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm index 97c36b0034..551bdbed05 100644 --- a/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm +++ b/Sources/Controllers/TargetMenu/OATargetInfoViewController.mm @@ -130,7 +130,6 @@ @implementation OATargetInfoViewController UIColor *_contentColor; NSArray *_nearestWiki; NSArray *_nearestPoi; - BOOL _hasOsmWiki; CGFloat _calculatedWidth; OAAmenityInfoRow *_onlinePhotoCardsRowInfo; @@ -138,6 +137,7 @@ @implementation OATargetInfoViewController BOOL _otherCardsReady; BOOL _isFetchingNearestPoi; + BOOL _isFetchingNearestWiki; } - (instancetype)init @@ -490,36 +490,65 @@ - (void)buildNearestWikiRow:(NSMutableArray *)rows - (void)buildNearestWikiRow:(NSMutableArray *)rows listener:(id)listener { - if ([OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]) + if (![OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]) + return; + + if (OAIAPHelper.sharedInstance.wiki.disabled) { - if (OAIAPHelper.sharedInstance.wiki.disabled) - { - [self buildGetWikipediaBanner:rows]; - } - else - { - OAPOI *poi = [self getTargetPoiIfExisted]; - if (poi) - { - [self processNearestWiki:poi]; - - NSArray *nearest = _nearestWiki; - NSString *rowText = [NSString stringWithFormat:@"%@ (%d)", OALocalizedString(@"wiki_around"), (int) nearest.count]; + [self buildGetWikipediaBanner:rows]; + return; + } - if (nearest.count > 0) - { - OAPOIUIFilter *wikiFilter = [self getPoiFilterForType:poi isWiki:YES]; - UIImage *icon = [UIImage mapSvgImageNamed:@"mx_wiki_place"]; - OAAmenityInfoRow *rowInfo = [[OAAmenityInfoRow alloc] initWithKey:nil icon:icon textPrefix:nil text:rowText textColor:nil isText:NO needLinks:NO order:kOrderNearestRow typeName:@"" isPhoneNumber:NO isUrl:NO]; - rowInfo.collapsed = YES; - rowInfo.collapsableView = [[OACollapsableNearestPoiWikiView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; - [((OACollapsableNearestPoiWikiView *) rowInfo.collapsableView) setData:nearest hasItems:(_hasOsmWiki) latitude:self.location.latitude longitude:self.location.longitude filter:wikiFilter]; - rowInfo.order = kOrderNearestRow; - [rows addObject:rowInfo]; - } - } - } + if (OARowsContainKey(rows, @"nearest_wiki")) + return; + + if (_isFetchingNearestWiki) + return; + + OAPOI *poi = [self getTargetPoiIfExisted]; + if (!poi) + return; + + OAPOIUIFilter *wikiFilter = [self getPoiFilterForType:poi isWiki:YES]; + if (!wikiFilter) + return; + + if (_nearestWiki) + { + [self addNearestWikiRowIfNeeded:rows poi:poi filter:wikiFilter]; + return; + } + + OAWikipediaPlugin *wikiPlugin = (OAWikipediaPlugin *) [OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]; + BOOL (^acceptByLanguage)(OAPOI *) = nil; + if (wikiPlugin && ![wikiPlugin isShowAllLanguages] && [wikiPlugin hasLanguagesFilter]) + { + NSMutableArray *languagesToShow = [[wikiPlugin getLanguagesToShow] mutableCopy]; + NSUInteger enIndex = [languagesToShow indexOfObject:@"en"]; + if (enIndex != NSNotFound) + [languagesToShow replaceObjectAtIndex:enIndex withObject:@""]; + acceptByLanguage = ^BOOL(OAPOI *w) { + return [w.localizedContent.allKeys firstObjectCommonWithArray:languagesToShow] != nil; + }; } + + _isFetchingNearestWiki = YES; + __weak __typeof(self) weakSelf = self; + [self fetchNearestWiki:poi accept:acceptByLanguage completion:^(NSArray *results) { + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) + return; + + strongSelf->_nearestWiki = [results copy]; + + NSMutableArray *currentRows = strongSelf->_rows; + if (results.count > 0 && currentRows && !OARowsContainKey(currentRows, @"nearest_wiki")) + { + [strongSelf addNearestWikiRowIfNeeded:currentRows poi:poi filter:wikiFilter]; + [strongSelf updateInfoRows]; + } + strongSelf->_isFetchingNearestWiki = NO; + }]; } - (void)buildGetWikipediaBanner:(NSMutableArray *)rows @@ -548,6 +577,9 @@ - (void)buildNearestPoiRow:(NSMutableArray *)rows listener:( if (!poi || ![self showNearestPoi]) return; + if (poi.type.category.isWiki) + return; + OAPOIUIFilter *filter = [self getPoiFilterForType:poi isWiki:NO]; if (!filter) return; @@ -559,16 +591,17 @@ - (void)buildNearestPoiRow:(NSMutableArray *)rows listener:( } _isFetchingNearestPoi = YES; __weak __typeof(self) weakSelf = self; - [self fetchNearestPoi:poi filter:filter completion:^(NSArray *results) { + [self fetchNearestPoi:poi filter:filter accept:nil completion:^(NSArray *results) { __strong __typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; strongSelf->_nearestPoi = [results copy]; - if (results.count > 0) + NSMutableArray *currentRows = strongSelf->_rows; + if (results.count > 0 && currentRows && !OARowsContainKey(currentRows, @"nearest_poi")) { - [strongSelf addNearestPoiRowIfNeeded:rows poi:poi filter:filter]; + [strongSelf addNearestPoiRowIfNeeded:currentRows poi:poi filter:filter]; [strongSelf updateInfoRows]; } strongSelf->_isFetchingNearestPoi = NO; @@ -618,6 +651,48 @@ - (void)addNearestPoiRowIfNeeded:(NSMutableArray *)rows [rows addObject:rowInfo]; } +- (void)addNearestWikiRowIfNeeded:(NSMutableArray *)rows + poi:(OAPOI *)poi + filter:(OAPOIUIFilter *)filter +{ + NSArray *nearest = _nearestWiki; + if (nearest.count == 0) + return; + + NSString *rowText = [NSString stringWithFormat:@"%@ (%d)", + OALocalizedString(@"wiki_around"), + (int)nearest.count]; + + OAAmenityInfoRow *rowInfo = [[OAAmenityInfoRow alloc] + initWithKey:@"nearest_wiki" + icon:[UIImage mapSvgImageNamed:@"mx_wiki_place"] + textPrefix:nil + text:rowText + textColor:nil + isText:NO + needLinks:NO + order:kOrderNearestRow + typeName:@"" + isPhoneNumber:NO + isUrl:NO]; + + rowInfo.collapsed = YES; + rowInfo.order = kOrderNearestRow; + + OACollapsableNearestPoiWikiView *view = + [[OACollapsableNearestPoiWikiView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; + + [view setData:nearest + hasItems:YES + latitude:self.location.latitude + longitude:self.location.longitude + filter:filter]; + + rowInfo.collapsableView = view; + + [rows addObject:rowInfo]; +} + static inline BOOL OARowsContainKey(NSArray *rows, NSString *key) { for (OAAmenityInfoRow *row in rows) @@ -849,95 +924,58 @@ - (void) rebuildRows [self buildMenu:[NSMutableArray array]]; } -- (void)processNearestWiki:(OAPOI *)poi -{ - int radius = kNearbyPoiMinRadius; - OsmAnd::PointI locI = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(self.location.latitude, self.location.longitude)); - NSMutableArray *osmwiki = [NSMutableArray new]; - OAWikipediaPlugin *wikiPlugin = (OAWikipediaPlugin *) [OAPluginsHelper getEnabledPlugin:OAWikipediaPlugin.class]; - NSMutableArray *languagesToShow = [[wikiPlugin getLanguagesToShow] mutableCopy]; - if ([languagesToShow containsObject:@"en"]) - { - NSInteger index = [languagesToShow indexOfObject:@"en"]; - [languagesToShow replaceObjectAtIndex:index withObject:@""]; - } - - while (osmwiki.count < kNearbyPoiMaxCount && radius <= kNearbyPoiMaxRadius) - { - osmwiki = [[OAAmenitySearcher findPOIsByTagName:nil name:nil location:locI categoryName:OSM_WIKI_CATEGORY poiTypeName:nil radius:radius] mutableCopy]; - [osmwiki removeObject:poi]; - - if (![wikiPlugin isShowAllLanguages] && [wikiPlugin hasLanguagesFilter]) - { - NSMutableArray *itemsToRemove = [NSMutableArray new]; - for (OAPOI *w in osmwiki) - { - if (![w.localizedContent.allKeys firstObjectCommonWithArray:languagesToShow]) - [itemsToRemove addObject:w]; - } - [osmwiki removeObjectsInArray:itemsToRemove]; - } - - radius *= kNearbyPoiSearchFactory; - } - osmwiki = [[OAMapUtils sortPOI:osmwiki lat:self.location.latitude lon:self.location.longitude] mutableCopy]; - - _hasOsmWiki = osmwiki.count > 0 && [osmwiki firstObjectCommonWithArray:osmwiki]; - _nearestWiki = [NSArray arrayWithArray:[osmwiki subarrayWithRange:NSMakeRange(0, MIN(kNearbyPoiMaxCount, osmwiki.count))]]; -} - - (void)fetchNearestPoi:(OAPOI *)poi filter:(OAPOIUIFilter *)filter + accept:(BOOL (^)(OAPOI *poi))accept completion:(void(^)(NSArray *results))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSMutableArray *amenities = [NSMutableArray new]; - if (!poi.type.category.isWiki) + int radius = kNearbyPoiMinRadius; + OsmAnd::PointI pointI = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(self.location.latitude, self.location.longitude)); + + while (amenities.count < kNearbyPoiMaxCount && radius <= kNearbyPoiMaxRadius) { - int radius = kNearbyPoiMinRadius; - OsmAnd::PointI pointI = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(self.location.latitude, self.location.longitude)); + const auto rect = OsmAnd::Utilities::boundingBox31FromAreaInMeters(radius, pointI); - while (amenities.count < kNearbyPoiMaxCount && radius <= kNearbyPoiMaxRadius) + NSArray *found = [filter searchAmenities:OsmAnd::Utilities::get31LatitudeY(rect.top()) + left:OsmAnd::Utilities::get31LongitudeX(rect.left()) + bottom:OsmAnd::Utilities::get31LatitudeY(rect.bottom()) + right:OsmAnd::Utilities::get31LongitudeX(rect.right()) + zoom:-1 matcher:nil filterUnique:YES]; + + if (found.count > 0) { - const auto rect = OsmAnd::Utilities::boundingBox31FromAreaInMeters(radius, pointI); - - NSArray *found = [filter searchAmenities:OsmAnd::Utilities::get31LatitudeY(rect.top()) - left:OsmAnd::Utilities::get31LongitudeX(rect.left()) - bottom:OsmAnd::Utilities::get31LatitudeY(rect.bottom()) - right:OsmAnd::Utilities::get31LongitudeX(rect.right()) - zoom:-1 matcher:nil filterUnique:YES]; - - if (found.count > 0) + for (OAPOI *a in found) { - for (OAPOI *a in found) - { - if (![amenities containsObject:a]) { - [amenities addObject:a]; - } + if (accept && !accept(a)) + continue; + if (![amenities containsObject:a]) { + [amenities addObject:a]; } } - - if (amenities.count >= kNearbyPoiMaxCount) - break; - radius *= kNearbyPoiSearchFactory; - } - - NSMutableArray *filteredAmenities = [NSMutableArray new]; - NSInteger osmObfId = [ObfConstants getOsmObjectId:poi]; - for (OAPOI *amenity in amenities) - { - if ([ObfConstants getOsmObjectId:amenity] != osmObfId) - [filteredAmenities addObject:amenity]; } - NSArray *sorted = [OAMapUtils sortPOI:filteredAmenities lat:self.location.latitude lon:self.location.longitude]; - - NSUInteger finalCount = MIN(kNearbyPoiMaxCount, sorted.count); - amenities = [[sorted subarrayWithRange:NSMakeRange(0, finalCount)] mutableCopy]; + if (amenities.count >= kNearbyPoiMaxCount) + break; + radius *= kNearbyPoiSearchFactory; + } + + NSMutableArray *filteredAmenities = [NSMutableArray new]; + NSInteger osmObfId = [ObfConstants getOsmObjectId:poi]; + for (OAPOI *amenity in amenities) + { + if ([ObfConstants getOsmObjectId:amenity] != osmObfId) + [filteredAmenities addObject:amenity]; } + NSArray *sorted = [OAMapUtils sortPOI:filteredAmenities lat:self.location.latitude lon:self.location.longitude]; + + NSUInteger finalCount = MIN(kNearbyPoiMaxCount, sorted.count); + amenities = [[sorted subarrayWithRange:NSMakeRange(0, finalCount)] mutableCopy]; + dispatch_async(dispatch_get_main_queue(), ^{ if (completion) { completion([amenities copy]); @@ -946,6 +984,52 @@ - (void)fetchNearestPoi:(OAPOI *)poi }); } +- (void)fetchNearestWiki:(OAPOI *)poi + accept:(BOOL (^)(OAPOI *poi))accept + completion:(void(^)(NSArray *results))completion +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + + NSMutableArray *osmwiki = [NSMutableArray new]; + int radius = kNearbyPoiMinRadius; + OsmAnd::PointI locI = OsmAnd::Utilities::convertLatLonTo31(OsmAnd::LatLon(self.location.latitude, self.location.longitude)); + + while (osmwiki.count < kNearbyPoiMaxCount && radius <= kNearbyPoiMaxRadius) + { + osmwiki = [[OAAmenitySearcher findPOIsByTagName:nil + name:nil + location:locI + categoryName:OSM_WIKI_CATEGORY + poiTypeName:nil + radius:radius] mutableCopy]; + [osmwiki removeObject:poi]; + + if (accept) + { + NSMutableArray *itemsToRemove = [NSMutableArray new]; + for (OAPOI *w in osmwiki) + { + if (!accept(w)) + [itemsToRemove addObject:w]; + } + [osmwiki removeObjectsInArray:itemsToRemove]; + } + + radius *= kNearbyPoiSearchFactory; + } + + NSArray *sorted = [OAMapUtils sortPOI:osmwiki lat:self.location.latitude lon:self.location.longitude]; + NSUInteger finalCount = MIN(kNearbyPoiMaxCount, sorted.count); + NSArray *result = [sorted subarrayWithRange:NSMakeRange(0, finalCount)]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(result); + } + }); + }); +} + - (BOOL) showNearestWiki { return YES; diff --git a/Sources/Controllers/TargetMenu/OATargetMenuViewController.h b/Sources/Controllers/TargetMenu/OATargetMenuViewController.h index 391bf595af..4cf5c21155 100644 --- a/Sources/Controllers/TargetMenu/OATargetMenuViewController.h +++ b/Sources/Controllers/TargetMenu/OATargetMenuViewController.h @@ -29,6 +29,7 @@ typedef void (^ContentHeightChangeListenerBlock)(CGFloat newHeight); - (void) contentHeightChanged; - (void) contentChanged; - (void) addressLabelUpdated; +- (void)refreshTargetPointHeader; - (void) btnOkPressed; - (void) btnCancelPressed; diff --git a/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm b/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm index 17f1f15319..579d9d5189 100644 --- a/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm +++ b/Sources/Controllers/TargetMenu/OATargetMenuViewController.mm @@ -179,37 +179,16 @@ + (OATargetMenuViewController *)createMenuController:(OATargetPoint *)targetPoin controller = [[OAPOIViewController alloc] initWithPOI:targetPoint.targetObj]; if (selectedObject && [selectedObject isKindOfClass:BaseDetailsObject.class]) { - BaseDetailsObject *detailsObject = [OAAmenitySearcher.sharedInstance searchDetailedObject:selectedObject]; - if (detailsObject) - { - controller = [[PlaceDetailsViewController alloc] initWithPoi:targetPoint.targetObj detailsObject:detailsObject renderedObject:targetPoint.targetObj]; - } - else - { - controller = [[RenderedObjectViewController alloc] initWithRenderedObject:targetPoint.targetObj]; - } + controller = [[PlaceDetailsViewController alloc] initWithPoi:targetPoint.targetObj detailsObject:selectedObject renderedObject:targetPoint.targetObj]; } - - break; - } - case OATargetRenderedObject: - { - BaseDetailsObject *detailsObject = [OAAmenitySearcher.sharedInstance searchDetailedObject:selectedObject]; - if (detailsObject) - { - controller = [[PlaceDetailsViewController alloc] initWithPoi:targetPoint.targetObj detailsObject:detailsObject renderedObject:targetPoint.targetObj]; - } - else - { - controller = [[RenderedObjectViewController alloc] initWithRenderedObject:targetPoint.targetObj]; - } break; } + case OATargetRenderedObject: case OATargetLocation: { - controller = [[RenderedObjectViewController alloc] initWithRenderedObject:targetPoint.targetObj]; + controller = [[PlaceDetailsViewController alloc] initWithRenderedObject:targetPoint.targetObj]; break; } diff --git a/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift b/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift index e9909b7147..7a4523cf22 100644 --- a/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift +++ b/Sources/Controllers/TargetMenu/POI/PlaceDetailsViewController.swift @@ -26,14 +26,26 @@ final class PlaceDetailsViewController: OAPOIViewController { self.provider = RenderedObjectAmenityProvider(detailsObject: detailsObject, renderedObject: renderedObject) setObject(detailsObject) } - + + init(renderedObject: OARenderedObject) { + let poi = BaseDetailsObject.convertRenderedObjectToAmenity(renderedObject) + super.init(poi: poi) + self.renderedObject = renderedObject + self.provider = RenderedObjectAmenityProvider(renderedObject: renderedObject) + } + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: "OAPOIViewController", bundle: nibBundleOrNil) } - + override func viewDidLoad() { - updateMenuWithDetailedObject() + if detailsObject != nil { + updateMenuWithDetailedObject() + } super.viewDidLoad() + if detailsObject == nil { + resolveDetailedObjectInBackground() + } } override func setObject(_ object: Any) { @@ -55,16 +67,30 @@ final class PlaceDetailsViewController: OAPOIViewController { } override func getTypeStr() -> String? { - let typeString = provider.typeString() { super.getTypeStr() } + let typeString = provider.typeString { super.getTypeStr() } return typeString ?? super.getTypeStr() } - + + override func getIcon() -> UIImage? { + guard detailsObject == nil, let renderedObject else { return super.getIcon() } + return RenderedObjectHelper.getIcon(renderedObject: renderedObject) + } + + override func getOsmUrl() -> String { + guard detailsObject == nil, let renderedObject else { return super.getOsmUrl() } + return ObfConstants.getOsmUrlForId(renderedObject) + } + override func buildPhotosRow(_ rows: NSMutableArray) { super.buildPhotosRow(rows) buildGuidesRow() } override func buildDescription(_ rows: NSMutableArray) { + if detailsObject == nil { + super.buildDescription(rows) + return + } let wikiAmenities = getWikiAmenities() var hasDescription = buildDescription(amenities: wikiAmenities, allowOnlineWiki: false, rows: rows) @@ -204,11 +230,36 @@ final class PlaceDetailsViewController: OAPOIViewController { setup(amenity) updateTargetPoint(with: amenity) } - + + private func resolveDetailedObjectInBackground() { + guard let renderedObject else { return } + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + let details = OAAmenitySearcher.sharedInstance().searchDetailedObject(renderedObject) + DispatchQueue.main.async { + guard let self, + let details, + let tableView = self.tableView, + let mapPanel = OARootViewController.instance()?.mapPanel, + let targetPoint = mapPanel.getCurrentTargetPoint(), + (targetPoint.targetObj as AnyObject) === renderedObject + else { return } + self.detailsObject = details + self.provider.detailsObject = details + let amenity = details.syntheticAmenity + self.setup(amenity) + self.updateTargetPoint(with: amenity) + self.rebuildRows() + tableView.reloadData() + self.delegate?.refreshTargetPointHeader?() + } + } + } + private func updateTargetPoint(with amenity: OAPOI) { guard let mapPanel = OARootViewController.instance()?.mapPanel, let targetPoint = mapPanel.getCurrentTargetPoint() else { return } + targetPoint.title = amenity.nameLocalized ?? amenity.name targetPoint.icon = amenity.type?.icon() mapPanel.update(targetPoint) diff --git a/Sources/Controllers/TargetMenu/POI/RenderedObjectAmenityProvider.swift b/Sources/Controllers/TargetMenu/POI/RenderedObjectAmenityProvider.swift index 1f0919c209..a10f4fd1c6 100644 --- a/Sources/Controllers/TargetMenu/POI/RenderedObjectAmenityProvider.swift +++ b/Sources/Controllers/TargetMenu/POI/RenderedObjectAmenityProvider.swift @@ -9,7 +9,12 @@ @objcMembers final class RenderedObjectAmenityProvider: NSObject { - var detailsObject: BaseDetailsObject? + var detailsObject: BaseDetailsObject? { + didSet { + cachedNameStr = nil + cachedTypeStr = nil + } + } private var renderedObject: OARenderedObject? private var cachedNameStr: String? private var cachedTypeStr: String? @@ -102,6 +107,28 @@ final class RenderedObjectAmenityProvider: NSObject { return cachedNameStr ?? "" } + private func actualContentFromIconRes() -> String? { + guard let content = renderedObject?.iconRes, !content.isEmpty else { return nil } + if content == "osmand_steps" { + return "highway_steps" + } + return content + } + + private func searchObjectNameByIconRes() -> String? { + guard let content = actualContentFromIconRes() else { return nil } + let poiTranslator = OAPOIHelper.sharedInstance() + let parts = content.split(separator: "_").map(String.init) + for i in parts.indices { + let key = parts[i...].joined(separator: "_") + let translation = poiTranslator.translation(key, withDefault: false) + if let translation, !translation.isEmpty { + return translation + } + } + return nil + } + func typeString(superTypeProvider: (() -> String?)? = nil) -> String? { if let cachedTypeStr, !cachedTypeStr.isEmpty { return cachedTypeStr @@ -124,9 +151,12 @@ final class RenderedObjectAmenityProvider: NSObject { cachedTypeStr = searchObjectTypeByAmenityTags(amenity) } + if cachedTypeStr?.isEmpty ?? true { + cachedTypeStr = searchObjectNameByIconRes() + } + if cachedTypeStr?.isEmpty ?? true { let additionalInfoKeys = amenity?.getAdditionalInfoKeys() - cachedTypeStr = searchObjectNameByRawTags( tags: renderedObject.tags as? [String: String], additionalInfoKeys: additionalInfoKeys diff --git a/Sources/Controllers/TargetMenu/POI/RenderedObjectViewController.swift b/Sources/Controllers/TargetMenu/POI/RenderedObjectViewController.swift deleted file mode 100644 index f790ab8d41..0000000000 --- a/Sources/Controllers/TargetMenu/POI/RenderedObjectViewController.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// RenderedObjectViewController.swift -// OsmAnd -// -// Created by Max Kojin on 20/01/25. -// Copyright © 2025 OsmAnd. All rights reserved. -// - -// analog in android: RenderedObjectMenuBuilder.java - -@objcMembers -final class RenderedObjectViewController: OAPOIViewController { - - private var renderedObject: OARenderedObject? - private var detailedObject: BaseDetailsObject? - - private var cachedNameStr: String? - private var cachedTypeStr: String? - - private var provider: RenderedObjectAmenityProvider! - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - init(renderedObject: OARenderedObject) { - let poi = BaseDetailsObject.convertRenderedObjectToAmenity(renderedObject) - super.init(poi: poi) - self.renderedObject = renderedObject - provider = RenderedObjectAmenityProvider(renderedObject: renderedObject) - } - - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - super.init(nibName: "OAPOIViewController", bundle: nibBundleOrNil) - } - - override func viewDidLoad() { - updateMenuWithDetailedObject() - super.viewDidLoad() - } - - override func getNameStr() -> String? { - let name = provider.nameOnlyString() - - if !name.isEmpty { - return name - } - - return getTypeStr() - } - - override func getTypeStr() -> String? { - let typeString = provider.typeString() { super.getTypeStr() } - return typeString ?? super.getTypeStr() - } - - override func getIcon() -> UIImage? { - guard let renderedObject else { return super.getIcon() } - guard detailedObject == nil else { - return detailedObject?.syntheticAmenity.icon() - } - return RenderedObjectHelper.getIcon(renderedObject: renderedObject) - } - - override func getOsmUrl() -> String { - guard let renderedObject else { return super.getOsmUrl() } - return ObfConstants.getOsmUrlForId(renderedObject) - } - - private func updateMenuWithDetailedObject() { - guard let renderedObject else { return } - guard let details = OAAmenitySearcher.sharedInstance().searchDetailedObject(renderedObject) else { return } - detailedObject = details - provider.detailsObject = detailedObject - let amenity = details.syntheticAmenity - setup(amenity) - updateTargetPoint(with: amenity) - rebuildRows() - tableView.reloadData() - } - - private func updateTargetPoint(with amenity: OAPOI) { - guard let mapPanel = OARootViewController.instance()?.mapPanel, - let targetPoint = mapPanel.getCurrentTargetPoint() else { return } - - targetPoint.title = amenity.nameLocalized ?? amenity.name - targetPoint.icon = amenity.type?.icon() - - mapPanel.update(targetPoint) - } -} diff --git a/Sources/Views/OATargetPointView.mm b/Sources/Views/OATargetPointView.mm index 2be317a9e2..f6d6182024 100644 --- a/Sources/Views/OATargetPointView.mm +++ b/Sources/Views/OATargetPointView.mm @@ -887,7 +887,7 @@ - (void) show:(BOOL)animated onComplete:(void (^)(void))onComplete frame.origin.y = 0; } - [UIView animateWithDuration:0.3 animations:^{ + [UIView animateWithDuration:0.2 animations:^{ self.frame = frame; @@ -2368,6 +2368,11 @@ - (void) addressLabelUpdated [self updateAddressLabel]; } +- (void)refreshTargetPointHeader +{ + [self applyTargetObjectChanges]; +} + - (CGPoint) applyMode:(BOOL)applyOffset { CGPoint newOffset = self.contentOffset;