image-classifier-ts
Version:
Command line tool to auto-classify images, renaming them with appropriate labels. Uses Node and Google Vision API.
174 lines • 7.77 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MapDateToLocation = exports.AUTO_DATE_MAP_CSV_FILENAME = void 0;
var fs = require("fs");
var path = require("path");
var ImageLocation_1 = require("../model/ImageLocation");
var CsvWriter_1 = require("./CsvWriter");
var FileUtils_1 = require("./FileUtils");
var SimpleDate_1 = require("./SimpleDate");
var DATE_MAP_CSV_FILENAME = "mapDateToLocation.csv";
exports.AUTO_DATE_MAP_CSV_FILENAME = "mapDateToLocation.auto.csv";
var MapDateToLocation = /** @class */ (function () {
function MapDateToLocation() {
this.mapDateToLocations = new Map();
this.mapExactDateToLocation = new Map();
}
MapDateToLocation.parseFromCsvAtDirectory = function (pathToDirectory, options) {
var filepath = path.join(pathToDirectory, DATE_MAP_CSV_FILENAME);
return this.parseFromCsvAtPath(filepath, options);
};
MapDateToLocation.parseFromCsvAtPath = function (filepath, options) {
var map = new MapDateToLocation();
if (!fs.existsSync(filepath)) {
return map;
}
// expected format:
//
// # Dates are US format
// # Start Date, End Date, Location description
// 10/07/2018,20/07/2018,Rotterdam
var allText = fs.readFileSync(filepath, "utf8");
allText
.split("\n")
.map(function (l) { return l.trim(); })
.map(function (l, zeroBasedLineNo) {
return {
line: l,
zeroBasedLineNo: zeroBasedLineNo
};
})
.filter(function (l) { return !MapDateToLocation.isComment(l.line) && l.line.length > 0; })
.forEach(function (line) {
var columns = line.line.split(",").map(function (c) { return c.trim(); });
var expectedColumns = 3;
if (columns.length !== expectedColumns) {
throw new Error("line ".concat(line.zeroBasedLineNo +
1, ": expected ").concat(expectedColumns, " columns but got ").concat(columns.length));
}
var column = 0;
var simpleStartDate = SimpleDate_1.SimpleDate.parseFromMDY(columns[column++]);
var simpleEndDate = SimpleDate_1.SimpleDate.parseFromMDY(columns[column++]);
var date = simpleStartDate;
var location = columns[column++];
while (date.isLessThanOrEqualTo(simpleEndDate)) {
map.addLocationForDate(date, ImageLocation_1.ImageLocation.fromGivenLocation(location, options));
date = date.nextDay();
}
});
return map;
};
MapDateToLocation.isComment = function (line) {
return line.startsWith("#");
};
MapDateToLocation.prototype.writeToCsvAtPath = function (filePath) {
var writer = new CsvWriter_1.CsvWriter(filePath);
this.mapDateToLocations.forEach(function (value, key) {
value.forEach(function (dateAndLocation) {
writer.writeRow([
// File has a 'from' and 'to' date - simplest to just write out 1 row for each date:
dateAndLocation.date.toString(),
dateAndLocation.date.toString(),
dateAndLocation.location.toString()
]);
});
});
};
MapDateToLocation.prototype.removeFromDisk = function (filePath) {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
};
MapDateToLocation.prototype.addLocationForDate = function (date, location, allowOverwrite) {
if (allowOverwrite === void 0) { allowOverwrite = false; }
// Dates should not be *exactly* identical
if (this.mapExactDateToLocation.has(date.toString()) && !allowOverwrite) {
throw new Error("Already have an entry for date ".concat(date.toString()));
}
if (!location || location.completionScore === 0) {
throw new Error("No location set for date ".concat(date.toString()));
}
this.mapExactDateToLocation.set(date.toString(), location);
var dateOnlyAsText = date.toStringDateOnly();
if (!this.mapDateToLocations.has(dateOnlyAsText)) {
this.mapDateToLocations.set(dateOnlyAsText, []);
}
var dateAndLocationsThisDate = this.mapDateToLocations.get(dateOnlyAsText);
dateAndLocationsThisDate.push({
date: date,
location: location
});
// sort for simpler searching:
dateAndLocationsThisDate.sort(function (a, b) {
return a.date.compareTo(b.date);
});
};
MapDateToLocation.prototype.addLocationForDateAllowOverwrite = function (date, location) {
this.addLocationForDate(date, location, true);
};
MapDateToLocation.prototype.getLocationForDate = function (date) {
if (this.mapExactDateToLocation.has(date.toString())) {
return this.mapExactDateToLocation.get(date.toString());
}
if (this.mapDateToLocations.has(date.toStringDateOnly())) {
// Use case: mobile photos with locations are mixed in with non-mobile photos,
// and we want to match the non-mobile photos to the closest mobile ones, to use the location.
//
// Use exact match OR else the closest match (before or after!)
//
// Assumptions:
// - photos not taken around midnight
// - processing timezone close to the timezone of the photos
// alt solution: use a b-tree?
var sameDates = this.mapDateToLocations.get(date.toStringDateOnly());
if (sameDates.length === 0) {
return null;
}
var preceding = null;
var following = null;
// assumption: sameDates are sorted by time
for (var _i = 0, sameDates_1 = sameDates; _i < sameDates_1.length; _i++) {
var sameDate = sameDates_1[_i];
if (date.isGreaterThan(sameDate.date)) {
preceding = sameDate;
}
if (!following && date.isLessThan(sameDate.date)) {
following = sameDate;
}
// unlikely, but could happen
if (sameDate.date.isEqualTo(date)) {
return sameDate.location;
}
if (following) {
break;
}
}
if (preceding && !following) {
return preceding.location;
}
if (!preceding && following) {
return following.location;
}
if (preceding && following) {
// Pick the closest:
var timeAfterPreceding = Math.abs(date.compareTo(preceding.date));
var timeBeforeFollowing = Math.abs(following.date.compareTo(date));
if (timeAfterPreceding < timeBeforeFollowing) {
return preceding.location;
}
return following.location;
}
return null;
}
return null;
};
MapDateToLocation.prototype.getLocationForFile = function (filepath, outputter) {
var modified = FileUtils_1.FileUtils.getModificationDateOfFile(filepath, outputter);
var location = this.getLocationForDate(modified);
outputter.infoVerbose(" location for file - date ".concat(modified.toString(), " = '").concat(location, "'"));
return location;
};
return MapDateToLocation;
}());
exports.MapDateToLocation = MapDateToLocation;
//# sourceMappingURL=MapDateToLocation.js.map