hsexif 0.4.0.0 → 0.4.0.1
raw patch · 3 files changed
+68/−33 lines, 3 filesPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
API changes (from Hackage documentation)
+ Graphics.HsExif: readGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)
Files
- Graphics/HsExif.hs +55/−32
- hsexif.cabal +1/−1
- tests/Tests.hs +12/−0
Graphics/HsExif.hs view
@@ -13,6 +13,7 @@ getOrientation, ImageOrientation(..), RotationDirection(..),+ readGpsLatitudeLongitude, -- * The ExifValue type ExifValue(..),@@ -442,37 +443,37 @@ gpsTagOffset = exifIfd0Tag "gpsTagOffset" 0x8825 printImageMatching = exifIfd0Tag "printImageMatching" 0xc4a5 -gpsVersionID = exifGpsTag "gpsVersionID" 0x0000-gpsLatitudeRef = exifGpsTag "gpsLatitudeRef" 0x0001-gpsLatitude = exifGpsTag "gpsLatitude" 0x0002-gpsLongitudeRef = exifGpsTag "gpsLongitudeRef" 0x0003-gpsLongitude = exifGpsTag "gpsLongitude" 0x0004-gpsAltitudeRef = exifGpsTag "gpsAltitudeRef" 0x0005-gpsAltitude = exifGpsTag "gpsAltitude" 0x0006-gpsTimeStamp = exifGpsTag "gpsTimeStamp" 0x0007-gpsSatellites = exifGpsTag "gpsSatellites" 0x0008-gpsStatus = exifGpsTag "gpsStatus" 0x0009-gpsMeasureMode = exifGpsTag "gpsMeasureMode" 0x000a-gpsDop = exifGpsTag "gpsDop" 0x000b-gpsSpeedRef = exifGpsTag "gpsSpeedRef" 0x000c-gpsSpeed = exifGpsTag "gpsSpeed" 0x000d-gpsTrackRef = exifGpsTag "gpsTrackRef" 0x000e-gpsTrack = exifGpsTag "gpsTrack" 0x000f-gpsImgDirectionRef= exifGpsTag "gpsImgDirectionRef" 0x0010-gpsImgDirection = exifGpsTag "gpsImgDirection" 0x0011-gpsMapDatum = exifGpsTag "gpsMapDatum" 0x0012-gpsDestLatitudeRef= exifGpsTag "gpsDestLatitudeRef" 0x0013-gpsDestLatitude = exifGpsTag "gpsDestLatitude" 0x0014-gpsDestLongitudeRef= exifGpsTag "gpsDestLongitudeRef" 0x0015-gpsDestLongitude= exifGpsTag "gpsDestLongitude" 0x0016-gpsDestBearingRef= exifGpsTag "gpsDestBearingRef" 0x0017-gpsDestBearing = exifGpsTag "gpsDestBearing" 0x0018-gpsDestDistanceRef= exifGpsTag "gpsDestDistanceRef" 0x0019-gpsDestDistance = exifGpsTag "gpsDestDistance" 0x001a-gpsProcessingMethod= exifGpsTag "gpsProcessingMethod" 0x001b-gpsAreaInformation= exifGpsTag "gpsAreaInformation" 0x001c-gpsDateStamp = exifGpsTag "gpsDateStamp" 0x001d-gpsDifferential = exifGpsTag "gpsDifferential" 0x001e+gpsVersionID = exifGpsTag "gpsVersionID" 0x0000+gpsLatitudeRef = exifGpsTag "gpsLatitudeRef" 0x0001+gpsLatitude = exifGpsTag "gpsLatitude" 0x0002+gpsLongitudeRef = exifGpsTag "gpsLongitudeRef" 0x0003+gpsLongitude = exifGpsTag "gpsLongitude" 0x0004+gpsAltitudeRef = exifGpsTag "gpsAltitudeRef" 0x0005+gpsAltitude = exifGpsTag "gpsAltitude" 0x0006+gpsTimeStamp = exifGpsTag "gpsTimeStamp" 0x0007+gpsSatellites = exifGpsTag "gpsSatellites" 0x0008+gpsStatus = exifGpsTag "gpsStatus" 0x0009+gpsMeasureMode = exifGpsTag "gpsMeasureMode" 0x000a+gpsDop = exifGpsTag "gpsDop" 0x000b+gpsSpeedRef = exifGpsTag "gpsSpeedRef" 0x000c+gpsSpeed = exifGpsTag "gpsSpeed" 0x000d+gpsTrackRef = exifGpsTag "gpsTrackRef" 0x000e+gpsTrack = exifGpsTag "gpsTrack" 0x000f+gpsImgDirectionRef = exifGpsTag "gpsImgDirectionRef" 0x0010+gpsImgDirection = exifGpsTag "gpsImgDirection" 0x0011+gpsMapDatum = exifGpsTag "gpsMapDatum" 0x0012+gpsDestLatitudeRef = exifGpsTag "gpsDestLatitudeRef" 0x0013+gpsDestLatitude = exifGpsTag "gpsDestLatitude" 0x0014+gpsDestLongitudeRef = exifGpsTag "gpsDestLongitudeRef" 0x0015+gpsDestLongitude = exifGpsTag "gpsDestLongitude" 0x0016+gpsDestBearingRef = exifGpsTag "gpsDestBearingRef" 0x0017+gpsDestBearing = exifGpsTag "gpsDestBearing" 0x0018+gpsDestDistanceRef = exifGpsTag "gpsDestDistanceRef" 0x0019+gpsDestDistance = exifGpsTag "gpsDestDistance" 0x001a+gpsProcessingMethod = exifGpsTag "gpsProcessingMethod" 0x001b+gpsAreaInformation = exifGpsTag "gpsAreaInformation" 0x001c+gpsDateStamp = exifGpsTag "gpsDateStamp" 0x001d+gpsDifferential = exifGpsTag "gpsDifferential" 0x001e allExifTags :: [ExifTag] allExifTags = [exposureTime, fnumber, exposureProgram, isoSpeedRatings,@@ -611,7 +612,6 @@ \byteAlign components -> liftM ExifRationalList $ readManyInternal byteAlign components } --- ascii string is special for now. valueHandlers :: [ValueHandler] valueHandlers = [@@ -749,6 +749,29 @@ ExifNumber 7 -> Just $ MirrorRotation Ninety ExifNumber 8 -> Just $ Rotation Ninety _ -> Nothing++-- | Extract the GPS latitude and longitude where the picture was taken+-- (if it is present in the EXIF)+readGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)+readGpsLatitudeLongitude exifData = do+ (ExifText latRef) <- Map.lookup gpsLatitudeRef exifData+ latDec <- liftM gpsDecodeToDecimalDegrees $ Map.lookup gpsLatitude exifData+ let signedLatDec = case latRef of+ "S" -> -latDec+ _ -> latDec+ (ExifText longRef) <- Map.lookup gpsLongitudeRef exifData+ longDec <- liftM gpsDecodeToDecimalDegrees $ Map.lookup gpsLongitude exifData+ let signedLongDec = case longRef of+ "W" -> -longDec+ _ -> longDec+ return (signedLatDec, signedLongDec)++gpsDecodeToDecimalDegrees :: ExifValue -> Double+gpsDecodeToDecimalDegrees (ExifRationalList intPairs) = floatings !! 0 + floatings !! 1 / 60 + floatings !! 2 / 3600+ where+ floatings = fmap (uncurry intsToFloating) intPairs+ intsToFloating n d = (fromIntegral n) / (fromIntegral d)+gpsDecodeToDecimalDegrees _ = error "gpsDecodeToDecimalDegrees not called on a rational list!?" -- no way to prevent this at compile time??? -- $intro --
hsexif.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: hsexif-version: 0.4.0.0+version: 0.4.0.1 synopsis: EXIF handling library in pure Haskell description: The hsexif library provides functions for working with EXIF data contained in JPEG files. Currently it only supports reading the data.
tests/Tests.hs view
@@ -16,8 +16,10 @@ imageContents <- B.readFile "tests/test.jpg" noExif <- B.readFile "tests/noexif.jpg" png <- B.readFile "tests/test.png"+ gps <- B.readFile "tests/gps.jpg" let parseExifCorrect = (\(Right x) -> x) . parseExif let exifData = parseExifCorrect imageContents+ let gpsExifData = parseExifCorrect gps hspec $ do describe "not a JPG" $ testNotAJpeg png describe "no EXIF" $ testNoExif noExif@@ -25,6 +27,7 @@ describe "extract picture date" $ testDate exifData describe "image orientation" $ testOrientation exifData describe "read exif date time" $ testReadExifDateTime+ describe "read GPS lat long" $ testReadGpsLatLong gpsExifData testNotAJpeg :: B.ByteString -> Spec testNotAJpeg imageContents = it "returns empty list if not a JPEG" $@@ -108,5 +111,14 @@ assertEqual' (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33)) (readExifDateTime "2013:10:02 20:33:33") assertEqual' Nothing (readExifDateTime "2013:10:02 20:33:3") +testReadGpsLatLong :: Map ExifTag ExifValue -> Spec+testReadGpsLatLong exifData = it "reads gps latitude longitude" $ do+ let (Just (lat,long)) = readGpsLatitudeLongitude exifData+ assertBool' $ 50.2179 < lat && 50.2180 > lat+ assertBool' $ -5.031 > long && -5.032 < long + assertEqual' :: (Show a, Eq a) => a -> a -> Assertion assertEqual' = assertEqual "doesn't match"++assertBool' :: Bool -> Assertion+assertBool' = assertBool "doesn't match"