exiftool-vendored
Version:
Efficient, cross-platform access to ExifTool
188 lines • 8.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const node_child_process_1 = require("node:child_process");
const node_path_1 = require("node:path");
const node_util_1 = require("node:util");
const _chai_spec_1 = require("./_chai.spec");
const ExifTool_1 = require("./ExifTool");
const ExiftoolPath_1 = require("./ExiftoolPath");
const GeolocationTags_1 = require("./GeolocationTags");
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
async function getGeolocationTags(lat, lon) {
const exiftool = await (0, ExiftoolPath_1.exiftoolPath)();
const { stdout } = await execFileAsync(exiftool, [
"-api",
`geolocation=${lat},${lon}`,
"-json",
]);
const results = JSON.parse(stdout);
return results[0];
}
// Tags that are always returned by ExifTool for valid coordinates
const alwaysReturnedTags = new Set([
"GeolocationBearing",
"GeolocationCity",
"GeolocationCountry",
"GeolocationCountryCode",
"GeolocationDistance",
"GeolocationFeatureCode",
"GeolocationFeatureType",
"GeolocationPopulation",
"GeolocationPosition",
"GeolocationRegion",
"GeolocationSubregion",
"GeolocationTimeZone",
]);
// Tags that may not be returned in normal results
const optionalTags = new Set(["GeolocationWarning"]);
describe("GeolocationTags", function () {
this.timeout(10000);
this.slow(2000);
// Expected values from ExifTool - this validates tag name casing
const sfExploratorium = {
GeolocationBearing: 227,
GeolocationCity: "Chinatown",
GeolocationCountry: "United States",
GeolocationCountryCode: "US",
GeolocationDistance: "0.80 km",
GeolocationFeatureCode: "PPLX",
GeolocationFeatureType: "Section of Populated Place",
GeolocationPopulation: 100000,
GeolocationPosition: "37.7966, -122.4086",
GeolocationRegion: "California",
GeolocationSubregion: "City and County of San Francisco",
GeolocationTimeZone: "America/Los_Angeles",
GeolocationWarning: undefined, // Not returned for valid coordinates
};
const eiffelTower = {
GeolocationBearing: 319,
GeolocationCity: "Neuilly-sur-Seine",
GeolocationCountry: "France",
GeolocationCountryCode: "FR",
GeolocationDistance: "3.33 km",
GeolocationFeatureCode: "PPL",
GeolocationFeatureType: "Populated Place",
GeolocationPopulation: 61000,
GeolocationPosition: "48.8845, 2.2697",
GeolocationRegion: "Île-de-France",
GeolocationSubregion: "Hauts-de-Seine",
GeolocationTimeZone: "Europe/Paris",
GeolocationWarning: undefined,
};
// Southern hemisphere test case
const sydneyOperaHouse = {
GeolocationBearing: 251,
GeolocationCity: "The Rocks",
GeolocationCountry: "Australia",
GeolocationCountryCode: "AU",
GeolocationDistance: "0.69 km",
GeolocationFeatureCode: "PPLL",
GeolocationFeatureType: "Populated Locality",
GeolocationPopulation: 2100,
GeolocationPosition: "-33.8592, 151.2083",
GeolocationRegion: "New South Wales",
GeolocationSubregion: "City of Sydney",
GeolocationTimeZone: "Australia/Sydney",
GeolocationWarning: undefined,
};
const testLocations = [
{
name: "SF Exploratorium",
lat: 37.802078,
lon: -122.4026305,
expected: sfExploratorium,
},
{
name: "Eiffel Tower",
lat: 48.8583736,
lon: 2.2919064,
expected: eiffelTower,
},
{
name: "Sydney Opera House",
lat: -33.856784,
lon: 151.215297,
expected: sydneyOperaHouse,
},
];
for (const loc of testLocations) {
describe(loc.name, () => {
let result;
before(async () => {
result = await getGeolocationTags(loc.lat, loc.lon);
});
it("returns expected tag values", () => {
for (const tagName of GeolocationTags_1.GeolocationTagNames.values) {
if (optionalTags.has(tagName))
continue;
(0, _chai_spec_1.expect)(result[tagName]).to.eql(loc.expected[tagName], `Tag ${tagName} mismatch`);
}
});
it("all returned tag names match GeolocationTagNames casing", () => {
const returnedKeys = Object.keys(result).filter((k) => k !== "SourceFile");
const unknownTags = [];
const knownTagsSet = new Set(GeolocationTags_1.GeolocationTagNames.values);
for (const key of returnedKeys) {
if (!knownTagsSet.has(key)) {
unknownTags.push(key);
}
}
(0, _chai_spec_1.expect)(unknownTags).to.eql([], `ExifTool returned geolocation tags not in GeolocationTagNames: ${unknownTags.join(", ")}. ` +
`This may indicate a casing mismatch or missing tags.`);
});
it("all expected tags are returned by ExifTool", () => {
const returnedKeys = new Set(Object.keys(result));
for (const tagName of alwaysReturnedTags) {
(0, _chai_spec_1.expect)(returnedKeys.has(tagName)).to.eql(true, `Expected tag "${tagName}" not returned by ExifTool`);
}
});
});
}
describe("reading from image file", () => {
let et;
before(() => {
et = new ExifTool_1.ExifTool({ geolocation: true });
});
after(() => (0, _chai_spec_1.end)(et));
// Expected values when reading pixel.jpg with geolocation enabled
// Note: GeolocationPosition format differs from raw exiftool output because
// numericTags includes GeolocationPosition, so ExifTool uses -Tag# which
// returns "lat lon" instead of "lat, lon"
const pixelJpgExpected = {
GeolocationBearing: 318,
GeolocationCity: "El Granada",
GeolocationCountry: "United States",
GeolocationCountryCode: "US",
GeolocationDistance: "2.60 km",
GeolocationFeatureCode: "PPL",
GeolocationFeatureType: "Populated Place",
GeolocationPopulation: 5500,
GeolocationPosition: "37.5027 -122.4694",
GeolocationRegion: "California",
GeolocationSubregion: "San Mateo County",
GeolocationTimeZone: "America/Los_Angeles",
GeolocationWarning: undefined,
};
it("returns geolocation tags when reading pixel.jpg", async () => {
const tags = await et.read((0, node_path_1.join)(_chai_spec_1.testDir, "pixel.jpg"));
for (const tagName of GeolocationTags_1.GeolocationTagNames.values) {
if (optionalTags.has(tagName))
continue;
(0, _chai_spec_1.expect)(tags[tagName]).to.eql(pixelJpgExpected[tagName], `Tag ${tagName} mismatch`);
}
});
it("all returned geolocation tag names match GeolocationTagNames casing", async () => {
const tags = await et.read((0, node_path_1.join)(_chai_spec_1.testDir, "pixel.jpg"));
const returnedKeys = Object.keys(tags).filter((k) => k.startsWith("Geolocation"));
const unknownTags = [];
const knownTagsSet = new Set(GeolocationTags_1.GeolocationTagNames.values);
for (const key of returnedKeys) {
if (!knownTagsSet.has(key)) {
unknownTags.push(key);
}
}
(0, _chai_spec_1.expect)(unknownTags).to.eql([], `ExifTool returned geolocation tags not in GeolocationTagNames: ${unknownTags.join(", ")}`);
});
});
});
//# sourceMappingURL=GeolocationTags.spec.js.map