Sunday, December 22, 2024

Different ways for running python script in QGIS

QGIS was written in  C++, Python and Qt framework.

In this post, I will share with you the common places where you can use or run python script within QGIS. If you already know python and some basics of QGIS, then this will be great exposure to know how to automate different parts of the open source GIS software.

Python in QGIS can be used to interact and automate GIS data in:-

1) Python Console

Plugins >> Python Console (Ctrl+Alt+P)




2) Label Expression



3) Field Calculator



4) Processing Toolbox



5) Plugins



6) Stand alone QGIS Apps



7) Script Runner Plugin



8) Project Event - Open, Close and Save






Thursday, December 12, 2024

Preparing a Flag map of Africa 24 Teams Set For AFCON 2025 In Morocco

 This task consist of two parts, the GIS and Graphic Design parts. To complete the task, I will use two software namely QGIS for the GIS part of the project and GIMP for the graphic design part of the project.

Data Sources:

  • The Map is the world map from QGIS resource data
  • The Flags were made by Hampus Nilsson

Procedures
Step 1: Using the QGIS software, query the world map for African countries and the 24 teams that qualified for the AFCON 2025 as seen below.



Now we know the countries to be designed with flags respectively.

Step 2: Lets make sure the flags we downloaded are available for the 24 countries. The flag source uses two alphabets to name the flags, this could be challenging but fortunately there is a JSON file in the directory the associalte the two two alphabet code with the full name of the countries.


'BW, 'EG', 'ZR', 'SD', 'ZM', 'NG', 'BJ', 'UG', 'TZ', 'GA', 'CI', 'DZ', 'SN', 'CM', 'GQ', 'BF', 'MZ', 'ZA', 'MA', 'TN', 'AO', 'KM', 'ML', 'ZW'

The 'WB_A2' attribute column matches the names of the flags, so that is what we will use the get the names of the country for each flag.

Saturday, December 7, 2024

How to make country flag map in QGIS

Using graphics software like GIMP/Photoshop, Inkscape/Illustrator, we could fill polygons shapes with an image easily, now how do we do the same in QGIS?


Yes, QGIS is not a graphic software yet it has the capability to fill polygons shapes with an image. Let make use of a flag image to fill a country's polygon shape. I will use Nigerian flag and its boundary polygon shape for this demonstration however, you can prety much use any image with any polygon shape of your chosen.

In other words, all we are doing is to insert image in shape in QGIS. Lets see how we can achieve it.

First get the image flag and the vector polygon of Nigeria administrative boundaries.

Now, from the polygon's property window, open symbology tab and select 'Raster Image Fill' as seen below. You can now set path to the image, set the size of the image, set its opacity, etc as you require fit.


One important setting that determine if the image fills the entire polygon layer or each polygon shape in the layer is the "Coord mode" which can either be det to 'object' or 'viewport'.


With some tweaks and tricks, we can achieve what the graphics tool can do in QGIS. Though this kind of map designs are effectively done in the graphics software.

Happy mapping.

Sunday, December 1, 2024

50 Globe Projections Types in QGIS

 A globe projection, also known as a map projection, is a method for representing the Earth's surface on a flat surface, such as a map or computer screen.

Lets see the following 50 map projections in QGIS software;-
(1) Natural Earth Projection - EPSG: 54077
(2) Sphere Aitoff Projection - EPSG: 53043
(3) World Azimuthal Equidistant Projection - EPSG: 54032
(4) Sphere Behrmann Projection - EPSG: 53017
(5) Sphere Bonne Projection - EPSG: 53024
(6) World Cassini Projection - EPSG: 54028
(7) Sphere Compact Miller Projection - EPSG: 53080
(8) Sphere Craster Parabolic Projection - EPSG: 53046
(9) Equal Area Cylindrical Projection - EPSG: 54034
(10) Eckert-1 Projection - EPSG: 53015
(11) Eckert-2 Projection - EPSG: 53014
(12) Eckert-3 Projection - EPSG: 53013
(13) Eckert-4 Projection - EPSG: 53012
(14) Eckert-5 Projection - EPSG: 53011
(15) Eckert-6 Projection - EPSG: 53010
(16) Sphere Equal Earth Americas Projection - EPSG: 53036
(17) Africa Equidistant Conic Projection - EPSG: 102023
(18) World Equidistant Cylindrical Projection - EPSG: 4088
(19) Sphere Flat Polar Quartic Projection - EPSG: 53045
(20) Gall Stereographic Projection - EPSG: 53016
(21) Gall and Peters Projection - EPSG: 100000
(22) Goode Homolosine Land Projection - EPSG: 54052
(23) Google Maps Global Mercator Projection Projection - EPSG: 900913
(24) MILLER GEOPORTAIL Projection - EPSG: MILLER
(25) North Pole LAEA Alaska Projection Projection - EPSG: 3572
(26) American Samoa Lambert Projection - EPSG: 2155
(27) South Pole Orthographic Projection - EPSG: 102037
(28) North Pole Orthographic Projection - EPSG: 102035
(29) NSIDC EASE-Grid Global Projection - EPSG: 3410
(30) Patterson Projection - EPSG: 53079
(31) Plate Caree Projection - EPSG: 32662
(32) Panama Polyconic Projection - EPSG: 5472
(33) Polyconic Projection - EPSG: 53021
(34) Quartic Authalic Projection - EPSG: 53022
(35) ICS Robinson Projection - EPSG: 102926
(36) Africa Sinusoidal Projection - EPSG: 102011
(37) JAXA Snow Depth Polar Stereographic North Projection - EPSG: 5890
(38) Times Projection - EPSG: 53048
(39) Vanua Levu 1915 Projection - EPSG: 4748
(40) Vertical Perspective Projection - EPSG: 53049
(41) Wagner IV Projection - EPSG: 53074
(42) Winkel I Projection - EPSG: 53018
(43) Winkel II Projection - EPSG: 53019
(44) Winkel Tripel NGS Projection - EPSG: 53042
(45) MAGNA Leticia Amazonas 1994 Projection - EPSG: 102767
(46) Mexico ITRF2008 Projection - EPSG: 6363
(47) Equal Earth Asia Pacific Projection - EPSG: 53037
(48) France Metropolitaine projection Geoportail Projection - EPSG: GEOPORTALFXX
(49) RRAF 1991 Projection - EPSG: 4640
(50) S-JTSK Krovak East North  Projection - EPSG: 102067





Wednesday, September 25, 2024

Fantasy Maps, Fantasy Cartography, Fictional Map-making, or Geofiction

Over the years, I have encountered clients who needed fantasy maps for books or novels which they are writing. These authors usually contract me to develop an imaginative map to complement the characters or world they are building in their storyline.

I will like to call these type of fantasy maps as imaginative map or mystic maps or mythical maps or adventure maps or fictional maps or map of nowhere. Whatever you decide to call such maps, it is basically a map that doesn't represent any location in the real world.

In this post, let's take a look at: What is a fantasy map? What professionals are responsible for making fantasy map? How to make a fantasy map? What technical skills are required for making fantasy maps? What elements are required to be shown on a fantasy map?


What is a fantasy map?

According to Wikipedia, fantasy map is a type of map design that visually presents an imaginary world or concept, or represents a real-world geography in a fantastic style.


Where are fantasy maps use?

Fantasy maps are used in a variety of contexts, primarily where imagination and creativity play a significant role such as in novel/book, Role-Playing Game (RPG), film/drama, or just for fun story telling.


What professionals are responsible for making fantasy map?

Cartographers are the professionals primarily responsible for creating fantasy maps. While the term "cartographer" is often associated with real-world geography, it can also extend to the imaginative realms of fantasy.

In addition to cartographers, artists, writers, and game designers also contribute to the creation of fantasy maps. Artists visualize the map, bringing it to life with illustrations and colors. Writers develop the backstory and lore of the fantasy world, providing context for the map. Game designers incorporate the map into the gameplay, ensuring that it is both visually appealing and functional.


What technical skills are required for making fantasy maps?

Technical Skills for Making Fantasy Maps are;-

  • Drawing and Illustration skills
  • Design and Layout skills
  • Geography and Cartography skills


What elements are required to be shown on a fantasy map?

The Essential Elements for a Fantasy Map include: Landmasses, Bodies of water, Terrain, Borders, Roads, Enchanted forests, Compass rose, Legend or key.


How to make a fantasy map?

Since fantasy maps are not really representing a geographical location in reality, a GIS software won't be necessary. There are tools/software that specializes in making fantasy maps such as:

This tools mainly focus on bringing imaginations into map instead of enforcing geographic location representation.

Also if you are skilled in graphic design software such as Photoshop, GIMP, Inkscape, Coreldraw etc you could make fantasy maps.


Examples of fantasy maps

A fantastic examples of fantasy maps I found is that in the portfolio by Deranged Doctor Design (DDD). On the DDD portfolio page, you will see several examples of how a digital fantasy map was made out of hand drawn imagination.

Another source is the MapEffects.co by Josh Stolarz

Another example is "The Edge Chronicles started with the map"

Also meet Jessica Khoury with beautiful fictional maps in her portfolio.

Monday, September 2, 2024

Extracting image Geographic metadata in Python

 Here I got hundreds of pictures captured during a field trip. Each picture has its GPS or geographical coordinates embedded in its metadata. These pictures' metadata are stored in EXIF standard (read about other standards from this link). EXIF stands for Exchangeable Image File Format. It stores technical information about an image and its capture method, such as exposure settings, capture time, GPS location information and camera model.



The EXIF metadata can contain many attributes (approximately 270 attributes) as listed below. This attribute will depend on the camera capability and the purpose of the picture captured.

InteropIndex, ProcessingSoftware, NewSubfileType, SubfileType, ImageWidth, ImageLength, BitsPerSample, Compression, PhotometricInterpretation, Thresholding, CellWidth, CellLength, FillOrder, DocumentName, ImageDescription, Make, Model, StripOffsets, Orientation, SamplesPerPixel, RowsPerStrip, StripByteCounts, MinSampleValue, MaxSampleValue, XResolution, YResolution, PlanarConfiguration, PageName, FreeOffsets, FreeByteCounts, GrayResponseUnit, GrayResponseCurve, T4Options, T6Options, ResolutionUnit, PageNumber, TransferFunction, Software, DateTime, Artist, HostComputer, Predictor, WhitePoint, PrimaryChromaticities, ColorMap, HalftoneHints, TileWidth, TileLength, TileOffsets, TileByteCounts, SubIFDs, InkSet, InkNames, NumberOfInks, DotRange, TargetPrinter, ExtraSamples, SampleFormat, SMinSampleValue, SMaxSampleValue, TransferRange, ClipPath, XClipPathUnits, YClipPathUnits, Indexed, JPEGTables, OPIProxy, JPEGProc, JpegIFOffset, JpegIFByteCount, JpegRestartInterval, JpegLosslessPredictors, JpegPointTransforms, JpegQTables, JpegDCTables, JpegACTables, YCbCrCoefficients, YCbCrSubSampling, YCbCrPositioning, ReferenceBlackWhite, XMLPacket, RelatedImageFileFormat, RelatedImageWidth, RelatedImageLength, Rating, RatingPercent, ImageID, CFARepeatPatternDim, BatteryLevel, Copyright, ExposureTime, FNumber, IPTCNAA, ImageResources, ExifOffset, InterColorProfile, ExposureProgram, SpectralSensitivity, GPSInfo, ISOSpeedRatings, OECF, Interlace, TimeZoneOffset, SelfTimerMode, SensitivityType, StandardOutputSensitivity, RecommendedExposureIndex, ISOSpeed, ISOSpeedLatitudeyyy, ISOSpeedLatitudezzz, ExifVersion, DateTimeOriginal, DateTimeDigitized, OffsetTime, OffsetTimeOriginal, OffsetTimeDigitized, ComponentsConfiguration, CompressedBitsPerPixel, ShutterSpeedValue, ApertureValue, BrightnessValue, ExposureBiasValue, MaxApertureValue, SubjectDistance, MeteringMode, LightSource, Flash, FocalLength, Noise, ImageNumber, SecurityClassification, ImageHistory, TIFF/EPStandardID, MakerNote, UserComment, SubsecTime, SubsecTimeOriginal, SubsecTimeDigitized, AmbientTemperature, Humidity, Pressure, WaterDepth, Acceleration, CameraElevationAngle, XPTitle, XPComment, XPAuthor, XPKeywords, XPSubject, FlashPixVersion, ColorSpace, ExifImageWidth, ExifImageHeight, RelatedSoundFile, ExifInteroperabilityOffset, FlashEnergy, SpatialFrequencyResponse, FocalPlaneXResolution, FocalPlaneYResolution, FocalPlaneResolutionUnit, SubjectLocation, ExposureIndex, SensingMethod, FileSource, SceneType, CFAPattern, CustomRendered, ExposureMode, WhiteBalance, DigitalZoomRatio, FocalLengthIn35mmFilm, SceneCaptureType, GainControl, Contrast, Saturation, Sharpness, DeviceSettingDescription, SubjectDistanceRange, ImageUniqueID, CameraOwnerName, BodySerialNumber, LensSpecification, LensMake, LensModel, LensSerialNumber, CompositeImage, CompositeImageCount, CompositeImageExposureTimes, Gamma, PrintImageMatching, DNGVersion, DNGBackwardVersion, UniqueCameraModel, LocalizedCameraModel, CFAPlaneColor, CFALayout, LinearizationTable, BlackLevelRepeatDim, BlackLevel, BlackLevelDeltaH, BlackLevelDeltaV, WhiteLevel, DefaultScale, DefaultCropOrigin, DefaultCropSize, ColorMatrix1, ColorMatrix2, CameraCalibration1, CameraCalibration2, ReductionMatrix1, ReductionMatrix2, AnalogBalance, AsShotNeutral, AsShotWhiteXY, BaselineExposure, BaselineNoise, BaselineSharpness, BayerGreenSplit, LinearResponseLimit, CameraSerialNumber, LensInfo, ChromaBlurRadius, AntiAliasStrength, ShadowScale, DNGPrivateData, MakerNoteSafety, CalibrationIlluminant1, CalibrationIlluminant2, BestQualityScale, RawDataUniqueID, OriginalRawFileName, OriginalRawFileData, ActiveArea, MaskedAreas, AsShotICCProfile, AsShotPreProfileMatrix, CurrentICCProfile, CurrentPreProfileMatrix, ColorimetricReference, CameraCalibrationSignature, ProfileCalibrationSignature, AsShotProfileName, NoiseReductionApplied, ProfileName, ProfileHueSatMapDims, ProfileHueSatMapData1, ProfileHueSatMapData2, ProfileToneCurve, ProfileEmbedPolicy, ProfileCopyright, ForwardMatrix1, ForwardMatrix2, PreviewApplicationName, PreviewApplicationVersion, PreviewSettingsName, PreviewSettingsDigest, PreviewColorSpace, PreviewDateTime, RawImageDigest, OriginalRawFileDigest, SubTileBlockSize, RowInterleaveFactor, ProfileLookTableDims, ProfileLookTableData, OpcodeList1, OpcodeList2, OpcodeList3, NoiseProfile, SpatialFrequencyResponse, SubjectLocation, ExposureIndex, CFAPattern, FlashEnergy

In our case here, the EXIF metadata attribute we are interested in is the GPSInfo. As seen above, it contains the latitude, longitude and altitude of the picture.

Lets extract this attribute information using python.

We are going to use the PIL library to read the EXIF metadata into pandas dataframe. Lets import the required modules.

import glob
import pandas as pd
from PIL import Image, ExifTags
from PIL.ExifTags import TAGS


The dictionary of all available EXIF metadata attributes can be accessed using ExifTags.TAGS as follow:-

{1: 'InteropIndex', 11: 'ProcessingSoftware', 254: 'NewSubfileType', 255: 'SubfileType', 256: 'ImageWidth', 257: 'ImageLength', 258: 'BitsPerSample', 259: 'Compression', 262: 'PhotometricInterpretation', 263: 'Thresholding', 264: 'CellWidth', 265: 'CellLength', 266: 'FillOrder', 269: 'DocumentName', 270: 'ImageDescription', 271: 'Make', 272: 'Model', 273: 'StripOffsets', 274: 'Orientation', 277: 'SamplesPerPixel', 278: 'RowsPerStrip', 279: 'StripByteCounts', 280: 'MinSampleValue', 281: 'MaxSampleValue', 282: 'XResolution', 283: 'YResolution', 284: 'PlanarConfiguration', 285: 'PageName', 288: 'FreeOffsets', 289: 'FreeByteCounts', 290: 'GrayResponseUnit', 291: 'GrayResponseCurve', 292: 'T4Options', 293: 'T6Options', 296: 'ResolutionUnit', 297: 'PageNumber', 301: 'TransferFunction', 305: 'Software', 306: 'DateTime', 315: 'Artist', 316: 'HostComputer', 317: 'Predictor', 318: 'WhitePoint', 319: 'PrimaryChromaticities', 320: 'ColorMap', 321: 'HalftoneHints', 322: 'TileWidth', 323: 'TileLength', 324: 'TileOffsets', 325: 'TileByteCounts', 330: 'SubIFDs', 332: 'InkSet', 333: 'InkNames', 334: 'NumberOfInks', 336: 'DotRange', 337: 'TargetPrinter', 338: 'ExtraSamples', 339: 'SampleFormat', 340: 'SMinSampleValue', 341: 'SMaxSampleValue', 342: 'TransferRange', 343: 'ClipPath', 344: 'XClipPathUnits', 345: 'YClipPathUnits', 346: 'Indexed', 347: 'JPEGTables', 351: 'OPIProxy', 512: 'JPEGProc', 513: 'JpegIFOffset', 514: 'JpegIFByteCount', 515: 'JpegRestartInterval', 517: 'JpegLosslessPredictors', 518: 'JpegPointTransforms', 519: 'JpegQTables', 520: 'JpegDCTables', 521: 'JpegACTables', 529: 'YCbCrCoefficients', 530: 'YCbCrSubSampling', 531: 'YCbCrPositioning', 532: 'ReferenceBlackWhite', 700: 'XMLPacket', 4096: 'RelatedImageFileFormat', 4097: 'RelatedImageWidth', 4098: 'RelatedImageLength', 18246: 'Rating', 18249: 'RatingPercent', 32781: 'ImageID', 33421: 'CFARepeatPatternDim', 33423: 'BatteryLevel', 33432: 'Copyright', 33434: 'ExposureTime', 33437: 'FNumber', 33723: 'IPTCNAA', 34377: 'ImageResources', 34665: 'ExifOffset', 34675: 'InterColorProfile', 34850: 'ExposureProgram', 34852: 'SpectralSensitivity', 34853: 'GPSInfo', 34855: 'ISOSpeedRatings', 34856: 'OECF', 34857: 'Interlace', 34858: 'TimeZoneOffset', 34859: 'SelfTimerMode', 34864: 'SensitivityType', 34865: 'StandardOutputSensitivity', 34866: 'RecommendedExposureIndex', 34867: 'ISOSpeed', 34868: 'ISOSpeedLatitudeyyy', 34869: 'ISOSpeedLatitudezzz', 36864: 'ExifVersion', 36867: 'DateTimeOriginal', 36868: 'DateTimeDigitized', 36880: 'OffsetTime', 36881: 'OffsetTimeOriginal', 36882: 'OffsetTimeDigitized', 37121: 'ComponentsConfiguration', 37122: 'CompressedBitsPerPixel', 37377: 'ShutterSpeedValue', 37378: 'ApertureValue', 37379: 'BrightnessValue', 37380: 'ExposureBiasValue', 37381: 'MaxApertureValue', 37382: 'SubjectDistance', 37383: 'MeteringMode', 37384: 'LightSource', 37385: 'Flash', 37386: 'FocalLength', 37389: 'Noise', 37393: 'ImageNumber', 37394: 'SecurityClassification', 37395: 'ImageHistory', 37398: 'TIFF/EPStandardID', 37500: 'MakerNote', 37510: 'UserComment', 37520: 'SubsecTime', 37521: 'SubsecTimeOriginal', 37522: 'SubsecTimeDigitized', 37888: 'AmbientTemperature', 37889: 'Humidity', 37890: 'Pressure', 37891: 'WaterDepth', 37892: 'Acceleration', 37893: 'CameraElevationAngle', 40091: 'XPTitle', 40092: 'XPComment', 40093: 'XPAuthor', 40094: 'XPKeywords', 40095: 'XPSubject', 40960: 'FlashPixVersion', 40961: 'ColorSpace', 40962: 'ExifImageWidth', 40963: 'ExifImageHeight', 40964: 'RelatedSoundFile', 40965: 'ExifInteroperabilityOffset', 41483: 'FlashEnergy', 41484: 'SpatialFrequencyResponse', 41486: 'FocalPlaneXResolution', 41487: 'FocalPlaneYResolution', 41488: 'FocalPlaneResolutionUnit', 41492: 'SubjectLocation', 41493: 'ExposureIndex', 41495: 'SensingMethod', 41728: 'FileSource', 41729: 'SceneType', 41730: 'CFAPattern', 41985: 'CustomRendered', 41986: 'ExposureMode', 41987: 'WhiteBalance', 41988: 'DigitalZoomRatio', 41989: 'FocalLengthIn35mmFilm', 41990: 'SceneCaptureType', 41991: 'GainControl', 41992: 'Contrast', 41993: 'Saturation', 41994: 'Sharpness', 41995: 'DeviceSettingDescription', 41996: 'SubjectDistanceRange', 42016: 'ImageUniqueID', 42032: 'CameraOwnerName', 42033: 'BodySerialNumber', 42034: 'LensSpecification', 42035: 'LensMake', 42036: 'LensModel', 42037: 'LensSerialNumber', 42080: 'CompositeImage', 42081: 'CompositeImageCount', 42082: 'CompositeImageExposureTimes', 42240: 'Gamma', 50341: 'PrintImageMatching', 50706: 'DNGVersion', 50707: 'DNGBackwardVersion', 50708: 'UniqueCameraModel', 50709: 'LocalizedCameraModel', 50710: 'CFAPlaneColor', 50711: 'CFALayout', 50712: 'LinearizationTable', 50713: 'BlackLevelRepeatDim', 50714: 'BlackLevel', 50715: 'BlackLevelDeltaH', 50716: 'BlackLevelDeltaV', 50717: 'WhiteLevel', 50718: 'DefaultScale', 50719: 'DefaultCropOrigin', 50720: 'DefaultCropSize', 50721: 'ColorMatrix1', 50722: 'ColorMatrix2', 50723: 'CameraCalibration1', 50724: 'CameraCalibration2', 50725: 'ReductionMatrix1', 50726: 'ReductionMatrix2', 50727: 'AnalogBalance', 50728: 'AsShotNeutral', 50729: 'AsShotWhiteXY', 50730: 'BaselineExposure', 50731: 'BaselineNoise', 50732: 'BaselineSharpness', 50733: 'BayerGreenSplit', 50734: 'LinearResponseLimit', 50735: 'CameraSerialNumber', 50736: 'LensInfo', 50737: 'ChromaBlurRadius', 50738: 'AntiAliasStrength', 50739: 'ShadowScale', 50740: 'DNGPrivateData', 50741: 'MakerNoteSafety', 50778: 'CalibrationIlluminant1', 50779: 'CalibrationIlluminant2', 50780: 'BestQualityScale', 50781: 'RawDataUniqueID', 50827: 'OriginalRawFileName', 50828: 'OriginalRawFileData', 50829: 'ActiveArea', 50830: 'MaskedAreas', 50831: 'AsShotICCProfile', 50832: 'AsShotPreProfileMatrix', 50833: 'CurrentICCProfile', 50834: 'CurrentPreProfileMatrix', 50879: 'ColorimetricReference', 50931: 'CameraCalibrationSignature', 50932: 'ProfileCalibrationSignature', 50934: 'AsShotProfileName', 50935: 'NoiseReductionApplied', 50936: 'ProfileName', 50937: 'ProfileHueSatMapDims', 50938: 'ProfileHueSatMapData1', 50939: 'ProfileHueSatMapData2', 50940: 'ProfileToneCurve', 50941: 'ProfileEmbedPolicy', 50942: 'ProfileCopyright', 50964: 'ForwardMatrix1', 50965: 'ForwardMatrix2', 50966: 'PreviewApplicationName', 50967: 'PreviewApplicationVersion', 50968: 'PreviewSettingsName', 50969: 'PreviewSettingsDigest', 50970: 'PreviewColorSpace', 50971: 'PreviewDateTime', 50972: 'RawImageDigest', 50973: 'OriginalRawFileDigest', 50974: 'SubTileBlockSize', 50975: 'RowInterleaveFactor', 50981: 'ProfileLookTableDims', 50982: 'ProfileLookTableData', 51008: 'OpcodeList1', 51009: 'OpcodeList2', 51022: 'OpcodeList3', 51041: 'NoiseProfile', 37388: 'SpatialFrequencyResponse', 37396: 'SubjectLocation', 37397: 'ExposureIndex', 33422: 'CFAPattern', 37387: 'FlashEnergy'}

You can export this into a user friendly spreadsheet format using the following lines of code:-

df_exif_tags = pd.DataFrame([ExifTags.TAGS]).T
df_exif_tags.to_excel('EXIF.xlsx', index=True)

We can use this img._getexif().items() to access the specific EXIF metadata attributes available in a picture after reading it like so;-

images = glob.glob(r'Field Trip Map\30-08-2024_05-00-34_7579\*.jpg')

img = Image.open(images[0])
print(img._getexif().items())

Friday, August 9, 2024

Geo visualization of 114 oldest secondary schools in Nigeria

The list is given below;-



Let geocode the schools to have there latitude and longitude coordinates. That is what we need for the Geo visualization. There are many ways to do the geocode, including using tools like Google maps API, OpenStreetMap API etc. The updated table should look like the one below;-


Now, you can import the table into any of the GIS software, here I will use QGIS. If you used automated process to obtain the coordinates, there will certainly be outliers in the result. In my case, I found about two outliers results one fell somewhere in republic of Niger while the other was in Central Africa Republic. So, I manually adjusted them accordingly to be in their right locations within Nigeria.


At this point the 'Geo visualization' has been successfully completed. And obviously we could see the concentration of the schools in the South Western part of the country.

The next thing to do is to add more context to the visualization and conduct analysis based on the objective you have in mind. For example, we could use the 'Age' column to create a proportional point symbol map.


We can also add labels from the attribute table, add state boundaries or state capital locations. Statistical plots could also be added as well as other cartographic elements to enrich the visualization.


That is it!

Friday, August 2, 2024

Landsat Path Row for Nigeria

 The Landsat 8 or Landsat 9 satellite acquires or records images across the earth in 'scenes'. Each scene covers a square of about 185km by 185km (i.e. 115miles by 115miles). A scene is defined by Path and Row, the Path number is always given first, followed by the Row number.

This Path Row helps in defining the Landsat download scene in EarthExplorer. This Path Row can be accessed on the USGS web page in both shapefile or kml formats. The Path Row are described by the World Reference System (WRS) which is a global notation used in cataloging Landsat data. Landsat satellites 1, 2 and 3 followed WRS-1, and Landsat satellites 4,5,7, 8 and 9 follow WRS-2.

There are different Path Row numbers for Landsat satellite image acquired during the nighttime and daytime as seen below;-

Ascending (nighttime) Path Row






Descending (daytime) Path Row



Take note of the Path Row numbers for Nigeria (or your country) and anytime you have a project the requires you to make use of Landsat data, you can easily refer to the Path Row number map to get idea of the scene coverage on your study area.


Thank you for following.

Thursday, July 11, 2024

Plotting Proportional Symbol/Points Size Map in QGIS - Case study of African Teams With The Highest AFCON Medals In History

 Let look at the top 10 African countries with the highest AFCON medals and use Proportional Symbol Map to visualize them.

For this mapping task, we need the map of African and the medals table as seen below.



The updated table as the time of writing (i.e. after the 2023 AFCON) is as follow;-




Steps

(1) Get the vector map of Africa and link it to the medals table. The newer versions QGIS comes with the world map as seen above, you can extract the map of Africa from there. Use the join attribute to link the map attribute to the medal table.


(2) Create a point layer using the Centroids algorithm function. This algorithm creates a new point layer, with points representing the centroid of the geometries in an input layer. The attributes associated to each point in the output layer are the same ones associated to the original features.


(3) The centroid point layer should have the linked medals table from step1 above. If not, you can make the join at this point. There are ten (10) on the medals table, so our focus should be on those 10 countries.

(4) There are two ways to make the  "Proportional Point Symbol" map in QGIS. The first is to use "Single Symbol" under 'Symbology'. The second method is to use "Graduated" under 'Symbology'.

The difference is in creating a 'Data-defined Size Legend' as a 'Collapsed Legend'. While it seamlessly worked when you used "Single Symbol", it doesn't work when you used "Graduated". Lets take a look at each method one after the other:-

Using Single Symbol

From the point layer Symbology tab, select "Single Symbol" and set the "Size" using 'Assistant...' 


Inside the  'Assistant...' dialog window, select the attribute field to be used for the 'Proportional Point Symbol' under the Input section and then set other Output section as seen below. The preview will be displayed on the right-hand side. Also this will enable you set 'Data-defined Size Legend' as a 'Collapsed Legend' from the "Advanced" button.




Now we can have a legend image that looks like the one below:-



Using Graduated

With this method, from the point layer Symbology tab, select "Graduated" and set the "Method" to 'Size' as seen below.


This will create the promotional points symbols, though you won't be able to set "Data-defined Size Legend" to 'Collapsed Legend". 

With this method, the legend image will look like the one below:-



That is it!

Saturday, June 29, 2024

Extracting EXIF data from HEIC or HEIF image format

 According to Wikipedia, HEIC/HEIF stands for "High Efficiency Image File Format" and it is a container format for storing individual digital images and image sequences. The standard covers multimedia files that can also include other media streams, such as timed text, audio and video. 

In other words, HEIC is Apple's file extension for the HEIF file format. It is basically a replacement for JPEG (Joint Photographic Experts Group) image format. JPEG is pretty aged and lacks new advances in compression, so there is need for better option. This lead Apple to adopt a standard for HEIC in 2015 to replace JPEG. So in iOS 11, HEIC was introduced and in High Sierra on the Mac.


The HEIC image file format can contains series of images and it also contains good number of textual information (EXIF metadata - Exchangeable Image File Format) embedded in it such the GPS information when the picture was taken.

In this post, I will share how we can extract the GPS information from HEIC image using a python script. The script was adopted from this stackoverflow question "How to extract GPS location from HEIC files" and it is as follow:-

import pandas as pd
from PIL import Image
from pillow_heif import register_heif_opener

def get_exif(filename):
    image = Image.open(filename)
    image.verify()
    return image.getexif().get_ifd(0x8825)


def get_geotagging(exif):
    geo_tagging_info = {}
    if not exif:
        raise ValueError("No EXIF metadata found")
    else:
        gps_keys = ['GPSVersionID', 'GPSLatitudeRef', 'GPSLatitude', 'GPSLongitudeRef', 'GPSLongitude',
                    'GPSAltitudeRef', 'GPSAltitude', 'GPSTimeStamp', 'GPSSatellites', 'GPSStatus', 'GPSMeasureMode',
                    'GPSDOP', 'GPSSpeedRef', 'GPSSpeed', 'GPSTrackRef', 'GPSTrack', 'GPSImgDirectionRef',
                    'GPSImgDirection', 'GPSMapDatum', 'GPSDestLatitudeRef', 'GPSDestLatitude', 'GPSDestLongitudeRef',
                    'GPSDestLongitude', 'GPSDestBearingRef', 'GPSDestBearing', 'GPSDestDistanceRef', 'GPSDestDistance',
                    'GPSProcessingMethod', 'GPSAreaInformation', 'GPSDateStamp', 'GPSDifferential']

        for k, v in exif.items():
            try:
                geo_tagging_info[gps_keys[k]] = str(v)
            except IndexError:
                pass
        return geo_tagging_info


register_heif_opener()

my_image = 'IMG_8362.heic'
image_info = get_exif(my_image)
results = get_geotagging(image_info)

print(results)


To run the functions above on multiple images and save the result into a pandas dataframe, the resulting code will look like below:-

dataList = []
fname = []
for heic_img in folder_loc:
    print('Processing...', heic_img)
    fname.append(heic_img.split('\\')[-1])
    
    image_info = get_exif(heic_img)
    results = get_geotagging(image_info)
    
    dataList.append(results)
    
print('Done...')

df_heic = pd.DataFrame(dataList)

df_heic['File Name'] = fname
df_heic.to_excel('DataList.xlsx', index=False)
.
You should now have table that contain the GPS information as seen above.

If you look closely at the Latitude and Longitude columns, the values are not really in a familiar formats of either decimal degrees or degree munities and seconds, so we have to do some extra data cleaning to get into the right format.

For example, the first latitude is "(24.0, 50.0, 28.13)", but that isn't useful to most GIS software. It should either be in Decimal Degrees like this "24.841147222" or in Degree Munities and Seconds like this "24° 50' 28.13" ". The cleaning code should look like this:- 

heic_lat = '(24.0, 50.0, 28.13)'
new_lat = heic_lat.replace('(', '').replace(')', '').split(', ')

# Convert to DMS
new_lat_DMS = new_lat[0].replace('.0', '') +'° '+ new_lat[1].replace('.0', '') +"' "+ new_lat[2] +'" '
print(new_lat_DMS)

# Convert to DD
new_lat_DD = int(float(new_lat[0])) + int(float(new_lat[1]))/60 + float(new_lat[2])/3600
print(new_lat_DD)

That is it!