diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d82a0f..753c1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ Adheres to [Semantic Versioning](http://semver.org/). ## 6.0.3 (TBD) -* TBD +* sf-ios 4.1.4 +* Projection Geometry Utilities with geodesic path and envelope methods ## [6.0.2](https://github.com/ngageoint/simple-features-proj-ios/releases/tag/6.0.2) (11-13-2023) diff --git a/Podfile b/Podfile index 906ead6..4ec515d 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '12.0' target 'sf-proj-ios' do - pod 'sf-ios', '~> 4.1.3' + pod 'sf-ios', '~> 4.1.4' pod 'proj-ios', '~> 2.0.2' target 'sf-proj-iosTests' do diff --git a/sf-proj-ios.podspec b/sf-proj-ios.podspec index f689d89..464af44 100644 --- a/sf-proj-ios.podspec +++ b/sf-proj-ios.podspec @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.frameworks = 'Foundation' - s.dependency 'sf-ios', '~> 4.1.3' + s.dependency 'sf-ios', '~> 4.1.4' s.dependency 'proj-ios', '~> 2.0.2' end diff --git a/sf-proj-ios.xcodeproj/project.pbxproj b/sf-proj-ios.xcodeproj/project.pbxproj index ea4d3c8..69b8fad 100644 --- a/sf-proj-ios.xcodeproj/project.pbxproj +++ b/sf-proj-ios.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 041F18E124CB2291001311E4 /* SFPReadmeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 041F18E024CB2291001311E4 /* SFPReadmeTest.m */; }; 041F18E424CB3762001311E4 /* SFPSwiftReadmeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041F18E324CB3762001311E4 /* SFPSwiftReadmeTest.swift */; }; + 04228B3D2BB45E350084DFFD /* SFPProjectionGeometryUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 04228B3B2BB45E350084DFFD /* SFPProjectionGeometryUtils.h */; }; + 04228B3E2BB45E350084DFFD /* SFPProjectionGeometryUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 04228B3C2BB45E350084DFFD /* SFPProjectionGeometryUtils.m */; }; 042FC6571B963FE500549A4B /* sf_proj_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 042FC64B1B963FE500549A4B /* sf_proj_ios.framework */; }; 042FC6AF1B96421E00549A4B /* (null) in Headers */ = {isa = PBXBuildFile; }; 042FC6D31B96421E00549A4B /* sf-proj-ios-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 042FC69E1B96421E00549A4B /* sf-proj-ios-Prefix.pch */; }; @@ -38,6 +40,8 @@ 041F18E024CB2291001311E4 /* SFPReadmeTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFPReadmeTest.m; sourceTree = ""; }; 041F18E224CB3762001311E4 /* sf-proj-iosTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sf-proj-iosTests-Bridging-Header.h"; sourceTree = ""; }; 041F18E324CB3762001311E4 /* SFPSwiftReadmeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFPSwiftReadmeTest.swift; sourceTree = ""; }; + 04228B3B2BB45E350084DFFD /* SFPProjectionGeometryUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFPProjectionGeometryUtils.h; sourceTree = ""; }; + 04228B3C2BB45E350084DFFD /* SFPProjectionGeometryUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFPProjectionGeometryUtils.m; sourceTree = ""; }; 042FC64B1B963FE500549A4B /* sf_proj_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = sf_proj_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 042FC64F1B963FE500549A4B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 042FC6561B963FE500549A4B /* sf_proj_iosTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = sf_proj_iosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -108,6 +112,8 @@ 0493A59B2AE93913000B75E6 /* sf_proj_ios.swift */, 04F455FA209A47C600FC299E /* SFPGeometryTransform.h */, 04F45606209A47C700FC299E /* SFPGeometryTransform.m */, + 04228B3B2BB45E350084DFFD /* SFPProjectionGeometryUtils.h */, + 04228B3C2BB45E350084DFFD /* SFPProjectionGeometryUtils.m */, 042FC69E1B96421E00549A4B /* sf-proj-ios-Prefix.pch */, 042FC64E1B963FE500549A4B /* Supporting Files */, 0472B51A1C03590500496B87 /* sf-proj-ios-Bridging-Header.h */, @@ -179,6 +185,7 @@ 04F4560E209A47C800FC299E /* SFPGeometryTransform.h in Headers */, 042FC6D51B96453E00549A4B /* sf_proj_ios.h in Headers */, 042FC6D31B96421E00549A4B /* sf-proj-ios-Prefix.pch in Headers */, + 04228B3D2BB45E350084DFFD /* SFPProjectionGeometryUtils.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -344,6 +351,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 04228B3E2BB45E350084DFFD /* SFPProjectionGeometryUtils.m in Sources */, 04F4561A209A47C800FC299E /* SFPGeometryTransform.m in Sources */, 0493A59C2AE93913000B75E6 /* sf_proj_ios.swift in Sources */, ); diff --git a/sf-proj-ios/SFPProjectionGeometryUtils.h b/sf-proj-ios/SFPProjectionGeometryUtils.h new file mode 100644 index 0000000..b989dd8 --- /dev/null +++ b/sf-proj-ios/SFPProjectionGeometryUtils.h @@ -0,0 +1,43 @@ +// +// SFPProjectionGeometryUtils.h +// sf-proj-ios +// +// Created by Brian Osborn on 3/27/24. +// Copyright © 2024 NGA. All rights reserved. +// + +#import "PROJProjection.h" +#import "SFPoint.h" + +/** + * Projection Geometry Utilities + */ +@interface SFPProjectionGeometryUtils : NSObject + +/** + * Create a geodesic path of a points in the projection with a max distance + * between any two path points + * + * @param points + * points in the projection + * @param maxDistance + * max distance allowed between path points + * @param projection + * projection of the points + * @return geodesic path of points + */ ++(NSArray *) geodesicPathOfPoints: (NSArray *) points withMaxDistance: (double) maxDistance inProjection: (PROJProjection *) projection; + +/** + * Expand the vertical bounds of a geometry envelope in the projection by + * including geodesic bounds + * + * @param envelope + * geometry envelope + * @param projection + * projection of the envelope + * @return geodesic expanded geometry envelope + */ ++(SFGeometryEnvelope *) geodesicEnvelope: (SFGeometryEnvelope *) envelope inProjection: (PROJProjection *) projection; + +@end diff --git a/sf-proj-ios/SFPProjectionGeometryUtils.m b/sf-proj-ios/SFPProjectionGeometryUtils.m new file mode 100644 index 0000000..4f45a5e --- /dev/null +++ b/sf-proj-ios/SFPProjectionGeometryUtils.m @@ -0,0 +1,77 @@ +// +// SFPProjectionGeometryUtils.m +// sf-proj-ios +// +// Created by Brian Osborn on 3/27/24. +// Copyright © 2024 NGA. All rights reserved. +// + +#import "SFPProjectionGeometryUtils.h" +#import "PROJProjectionFactory.h" +#import "PROJProjectionConstants.h" +#import "SFPGeometryTransform.h" +#import "SFGeometryUtils.h" + +@implementation SFPProjectionGeometryUtils + +static PROJProjection *WGS_84_PROJECTION = nil; + ++(void) initialize{ + if(WGS_84_PROJECTION == nil){ + WGS_84_PROJECTION = [PROJProjectionFactory projectionWithEpsgInt:PROJ_EPSG_WORLD_GEODETIC_SYSTEM]; + } +} + ++(NSArray *) geodesicPathOfPoints: (NSArray *) points withMaxDistance: (double) maxDistance inProjection: (PROJProjection *) projection{ + + NSArray *geodesicPath = points; + + if(projection != nil){ + + // Reproject to WGS84 if not in degrees + if(![projection isUnit:PROJ_UNIT_DEGREES]){ + SFPGeometryTransform *toWGS84 = [SFPGeometryTransform transformFromProjection:projection andToProjection:WGS_84_PROJECTION]; + geodesicPath = [toWGS84 transformPoints:geodesicPath]; + } + + // Create the geodesic path + geodesicPath = [SFGeometryUtils geodesicPathOfPoints:geodesicPath withMaxDistance:maxDistance]; + + // Reproject back to the original projection + if(![projection isUnit:PROJ_UNIT_DEGREES]){ + SFPGeometryTransform *fromWGS84 = [SFPGeometryTransform transformFromProjection:WGS_84_PROJECTION andToProjection:projection]; + geodesicPath = [fromWGS84 transformPoints:geodesicPath]; + } + + } + + return geodesicPath; +} + ++(SFGeometryEnvelope *) geodesicEnvelope: (SFGeometryEnvelope *) envelope inProjection: (PROJProjection *) projection{ + + SFGeometryEnvelope *geodesic = envelope; + + if(projection != nil){ + + // Reproject to WGS84 if not in degrees + if(![projection isUnit:PROJ_UNIT_DEGREES]){ + SFPGeometryTransform *toWGS84 = [SFPGeometryTransform transformFromProjection:projection andToProjection:WGS_84_PROJECTION]; + geodesic = [toWGS84 transformGeometryEnvelope:geodesic]; + } + + // Expand the envelope for geodesic lines + geodesic = [SFGeometryUtils geodesicEnvelope:geodesic]; + + // Reproject back to the original projection + if(![projection isUnit:PROJ_UNIT_DEGREES]){ + SFPGeometryTransform *fromWGS84 = [SFPGeometryTransform transformFromProjection:WGS_84_PROJECTION andToProjection:projection]; + geodesic = [fromWGS84 transformGeometryEnvelope:geodesic]; + } + + } + + return geodesic; +} + +@end diff --git a/sf-proj-ios/sf-proj-ios-Bridging-Header.h b/sf-proj-ios/sf-proj-ios-Bridging-Header.h index 1feb6b2..b88642d 100644 --- a/sf-proj-ios/sf-proj-ios-Bridging-Header.h +++ b/sf-proj-ios/sf-proj-ios-Bridging-Header.h @@ -13,5 +13,6 @@ #import "proj-ios-Bridging-Header.h" #import "sf_proj_ios.h" #import "SFPGeometryTransform.h" +#import "SFPProjectionGeometryUtils.h" #endif /* sf_proj_ios_Bridging_Header_h */