Skip to content

Commit

Permalink
feat: auto-detect gcj-02 selection
Browse files Browse the repository at this point in the history
  • Loading branch information
LynMoe committed Nov 11, 2023
1 parent 21375b6 commit 85b47e1
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 68 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
- [x] 支持 JPEG 和 PNG 及各大相机厂商的主流RAW格式
- [x] 全平台支持
- [x] 支持国策局 GCJ-02 和 WGS-84 坐标系 (解决国内坐标漂移问题)
- [x] 自动检测国内外位置, 自动转换为对应坐标系

## DEMO

```shell
➜ nya-exif /path/to/image/folder/
2023-11-08 15:57:30.830962000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4076.JPG
2023-11-08 15:57:30.931190000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4062.JPG
2023-11-08 15:57:30.967376000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4089.JPG
2023-11-08 15:57:30.967376000 [WARN] <nya_exif::core::app:120>:Missing location for file _MGL9572.JPG, timestamp 1699257194
2023-11-11 12:46:08.337698000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4100.JPG] Location updated, lat: 34.7737885, lon: 131.9007701
2023-11-11 12:46:08.394225000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4114.JPG] Location updated, lat: 34.67844170666667, lon: 131.83647663733333
2023-11-11 12:46:08.434180000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4128.JPG] Location updated, lat: 34.68192337279844, lon: 131.8327970596869
⠂ [00:00:04] [###########################>-----------------------------------------------] 93/233 (6.7s)
```
Expand All @@ -31,15 +31,15 @@
```shell
# macOS 下, 一生足迹启动 iCloud 云备份, 可直接运行
nya-exif /path/to/images
nya-exif .

# 其他平台下, 需要将一生足迹数据目录拷贝至本地
nya-exif -f /path/to/life-path/data /path/to/images

# 若 ExifTool 安装路径不在 PATH 中, 手动指定可执行文件位置
nya-exif -b /path/to/exiftool /path/to/images

# 指定目标坐标系, 默认为中国 GCJ-02 坐标系, 如果照片拍摄地为海外需要指定为 WGS-84 坐标系
# 指定目标坐标系, 默认为自动检测, 如果在边境线附近需要手动指定
nya-exif -c wgs84 /path/to/images
```
Expand Down Expand Up @@ -111,7 +111,9 @@ Options:
[default: 600]

-c, --location-coordinate-target <LOCATION_COORDINATE_TARGET>
[default: gcj02]
Location GPS coordinate convert target

Specifies the target coordinate system for converting GPS coordinates. Default is Auto-detect.

Possible values:
- wgs84: Global coordinate system
Expand Down Expand Up @@ -150,6 +152,10 @@ Options:
> [!IMPORTANT]
> Location Reader返回的经纬度应该为地球坐标系(WGS84), 本工具会根据用户选择的坐标系进行转换
## Declaimer
本工具中附带的 `GCJ-02` 范围数据仅用于粗略地理位置判断, 不具有任何立场和政治倾向, 请勿用于其他用途
## License
[MIT](LICENSE) ©[Lyn](mailto://[email protected])
10 changes: 6 additions & 4 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

```shell
➜ nya-exif /path/to/image/folder/
2023-11-08 15:57:30.830962000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4076.JPG
2023-11-08 15:57:30.931190000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4062.JPG
2023-11-08 15:57:30.967376000 [INFO] <nya_exif::core::app:84>:Updating location for 20230908-_MGL4089.JPG
2023-11-11 12:46:08.337698000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4100.JPG] Location updated, lat: 34.7737885, lon: 131.9007701
2023-11-11 12:46:08.394225000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4114.JPG] Location updated, lat: 34.67844170666667, lon: 131.83647663733333
2023-11-11 12:46:08.434180000 [INFO] <nya_exif::core::app:130>:[20230908-_MGL4128.JPG] Location updated, lat: 34.68192337279844, lon: 131.8327970596869
⠂ [00:00:04] [###########################>-----------------------------------------------] 93/233 (6.7s)
```
Expand Down Expand Up @@ -110,7 +110,9 @@ Options:
[default: 600]

-c, --location-coordinate-target <LOCATION_COORDINATE_TARGET>
[default: gcj02]
Location GPS coordinate convert target

Specifies the target coordinate system for converting GPS coordinates. Default is Auto-detect.

Possible values:
- wgs84: Global coordinate system
Expand Down
60 changes: 34 additions & 26 deletions src/core/app.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use crate::exif_writer::{exiftool::ExifWriterExifTool, ExifWriterBase, ExifWriterParam};
use crate::location_reader::{life_path, LocationReaderBase, LocationReaderParam};
use crate::util::{file, location as lc};
use clap::ValueEnum;
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use simple_log::log::{info, warn};
use std::fmt::Write;
use std::path::{Path, PathBuf};
use simple_log::log::{warn, info, error};
use undrift_gps::wgs_to_gcj;
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use std::sync::mpsc::channel;
use ctrlc;
use crate::exif_writer::{exiftool::ExifWriterExifTool, ExifWriterBase, ExifWriterParam};
use crate::location_reader::{life_path, LocationReaderBase, LocationReaderParam};
use crate::util::file;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum ExifWriterType {
Expand All @@ -32,24 +30,19 @@ pub enum LocationGpsCoordinateTarget {

#[derive(Debug)]
pub struct AppParams {
pub operate_dir: PathBuf,
pub operate_dir: Vec<PathBuf>,
pub recursive: bool,
pub writer_type: ExifWriterType,
pub writer_bin_path: Option<PathBuf>,
pub location_reader_type: LocationReaderType,
pub location_file_path: Option<PathBuf>,
pub location_max_interval: u32,
pub location_gps_coordinate_target: LocationGpsCoordinateTarget,
pub location_gps_coordinate_target: Option<LocationGpsCoordinateTarget>,
pub overwrite_original: bool,
pub time_offset: i32,
}

pub fn run(params: AppParams) {
let (tx, rx) = channel();

ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
.expect("Error setting Ctrl-C handler");

let exif_param = ExifWriterParam {
binary_path: params.writer_bin_path.clone(),
};
Expand All @@ -74,7 +67,11 @@ pub fn run(params: AppParams) {
}
}

let fi = file::read_dir_files(params.operate_dir.as_ref(), true, true).unwrap();
let mut fi = vec![];
for dir in params.operate_dir {
let fi_ = file::read_dir_files(dir.as_ref(), true, true).unwrap();
fi.extend(fi_);
}

let pb = ProgressBar::new(fi.len() as u64);
pb.set_style(
Expand All @@ -96,9 +93,24 @@ pub fn run(params: AppParams) {
let time = exiftool.read_timestamp(filename);
let mut location = location_reader.get_location(time as i32);

match params.location_gps_coordinate_target {
LocationGpsCoordinateTarget::GCJ02 => {
if location.is_some() {
if location.is_some() {
if params.location_gps_coordinate_target.is_some() {
match params.location_gps_coordinate_target.unwrap() {
LocationGpsCoordinateTarget::GCJ02 => {
let mut lo = location.unwrap();
let (lat, lon) = wgs_to_gcj(lo.lat, lo.lon);
lo.lat = lat;
lo.lon = lon;

location = Some(lo);
}
_ => {}
}
} else {
let llo = location.clone().unwrap();
let result = lc::is_point_in_gcj_region((llo.lat, llo.lon));

if result {
let mut lo = location.unwrap();
let (lat, lon) = wgs_to_gcj(lo.lat, lo.lon);
lo.lat = lat;
Expand All @@ -107,7 +119,6 @@ pub fn run(params: AppParams) {
location = Some(lo);
}
}
_ => {}
}

if location.is_some() {
Expand All @@ -116,7 +127,10 @@ pub fn run(params: AppParams) {

pb.suspend(|| {
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap();
info!("[{}] Location updated, lat: {}, lon: {}", filename, location.lat, location.lon);
info!(
"[{}] Location updated, lat: {}, lon: {}",
filename, location.lat, location.lon
);
});
} else {
pb.suspend(|| {
Expand All @@ -126,12 +140,6 @@ pub fn run(params: AppParams) {
}

pb.set_position(now_state);

if rx.try_recv().is_ok() {
error!("Stopped by user");
pb.finish_with_message("Stopped");
return;
}
}

pb.finish_with_message("Finished");
Expand Down
49 changes: 28 additions & 21 deletions src/core/cli.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use clap::Parser;
use std::env;
use std::path::PathBuf;
use simple_log::log::debug;
use simple_log::log::{debug, error};
use simple_log::LogConfigBuilder;

use crate::core::app::{self, ExifWriterType, LocationReaderType, LocationGpsCoordinateTarget};
Expand All @@ -12,7 +11,7 @@ use crate::core::app::{self, ExifWriterType, LocationReaderType, LocationGpsCoor
#[command(version)]
struct Cli {
/// Path to photography files
path: Option<String>,
path: Vec<String>,

/// Turn on recursive mode
#[arg(short, long, default_value_t = true)]
Expand Down Expand Up @@ -53,8 +52,11 @@ struct Cli {
#[arg(short = 'i', long, default_value_t = 600)]
location_max_interval: u32,

#[arg(short = 'c', long, value_enum, default_value_t = LocationGpsCoordinateTarget::GCJ02)]
location_coordinate_target: LocationGpsCoordinateTarget,
/// Location GPS coordinate convert target
///
/// Specifies the target coordinate system for converting GPS coordinates. Default is Auto-detect.
#[arg(short = 'c', long, value_enum)]
location_coordinate_target: Option<LocationGpsCoordinateTarget>,

/// Overwrite original file
#[arg(short, long, default_value_t = true)]
Expand All @@ -77,14 +79,14 @@ pub fn run() {
let cli = Cli::parse();

let mut param = app::AppParams {
operate_dir: env::current_dir().unwrap(),
operate_dir: Vec::new(),
recursive: true,
writer_type: app::ExifWriterType::Exiftool,
writer_bin_path: None,
location_reader_type: app::LocationReaderType::LifePath,
location_file_path: None,
location_max_interval: 1800,
location_gps_coordinate_target: app::LocationGpsCoordinateTarget::WGS84,
location_gps_coordinate_target: None,
overwrite_original: false,
time_offset: 0,
};
Expand All @@ -101,43 +103,48 @@ pub fn run() {
simple_log::new(config).expect("Failed to init log");
}

if let Some(pwd) = cli.path {
debug!("Value for path: {}", pwd);
param.operate_dir = PathBuf::from(pwd);
if cli.path.len() > 0 {
debug!("[Arg] Value for path: {:?}", cli.path);
param.operate_dir = cli.path.iter().map(|x| PathBuf::from(x)).collect();
} else {
error!("No path specified");
std::process::exit(1);
}

debug!("Value for recursive: {}", cli.recursive);
debug!("[Arg] Value for recursive: {}", cli.recursive);
param.recursive = cli.recursive;

debug!("Value for writer_type: {:?}", cli.writer_type);
debug!("[Arg] Value for writer_type: {:?}", cli.writer_type);
param.writer_type = cli.writer_type;

if let Some(writer_bin_path) = cli.writer_bin_path {
debug!("Value for writer_bin_path: {}", writer_bin_path);
debug!("[Arg] Value for writer_bin_path: {}", writer_bin_path);
param.writer_bin_path = Some(PathBuf::from(writer_bin_path));
}

debug!("Value for location_reader_type: {:?}", cli.location_reader_type);
debug!("[Arg] Value for location_reader_type: {:?}", cli.location_reader_type);
param.location_reader_type = cli.location_reader_type;

if let Some(location_file_path) = cli.location_file_path {
debug!("Value for location_file_path: {}", location_file_path);
debug!("[Arg] Value for location_file_path: {}", location_file_path);
param.location_file_path = Some(PathBuf::from(location_file_path));
}

debug!("Value for location_max_interval: {}", cli.location_max_interval);
debug!("[Arg] Value for location_max_interval: {}", cli.location_max_interval);
param.location_max_interval = cli.location_max_interval;

debug!("Value for location_gps_coordinate_target: {:?}", cli.location_coordinate_target);
param.location_gps_coordinate_target = cli.location_coordinate_target;
if let Some(location_coordinate_target) = cli.location_coordinate_target {
debug!("[Arg] Value for location_gps_coordinate_target: {:?}", location_coordinate_target);
param.location_gps_coordinate_target = Some(location_coordinate_target);
}

debug!("Value for overwrite_original: {}", cli.overwrite_original);
debug!("[Arg] Value for overwrite_original: {}", cli.overwrite_original);
param.overwrite_original = cli.overwrite_original;

debug!("Value for time_offset: {}", cli.time_offset);
debug!("[Arg] Value for time_offset: {}", cli.time_offset);
param.time_offset = cli.time_offset;

debug!("Value for app params: {:?}", param);
debug!("[Arg] Value for app params: {:?}", param);

app::run(param);
}
4 changes: 2 additions & 2 deletions src/exif_writer/exiftool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl ExifWriterExifTool {
result.push_str(&line);
}

debug!("Exiftool arg: {:?}, result: {}", args, result);
debug!("[Exiftool] arg: {:?}, result: {}", args, result);

result
}
Expand Down Expand Up @@ -95,7 +95,7 @@ impl ExifWriterBase for ExifWriterExifTool {
let dt = DateTime::parse_from_str(&time_string, "%Y:%m:%d %H:%M:%S %z")
.expect("Failed to parse date time string");

debug!("Exiftool read timestamp: {}, time_string: {}", dt.timestamp(), time_string);
debug!("[Exiftool] time_string: {}, parse timestamp: {}", time_string, dt.timestamp());

dt.timestamp()
}
Expand Down
10 changes: 5 additions & 5 deletions src/location_reader/life_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ impl LocationReaderLiftPath {
}
file_path.push("backUpData.csv");

debug!("LifePath folder path: {:?}", file_path);
debug!("[LifePath] folder path: {:?}", file_path);

if !file_path.exists() {
error!("LifePath csv file not exists, path {:?}", file_path);
error!("[LifePath] CSV file not exists, path {:?}", file_path);
panic!();
}

Expand Down Expand Up @@ -76,8 +76,8 @@ impl LocationReaderBase for LocationReaderLiftPath {
.from_reader(&self.file);
let record2 = rdr.records().next()?.unwrap();

debug!("record1: {:?}", record1);
debug!("record2: {:?}", record2);
debug!("[LifePath] search record1: {:?}", record1);
debug!("[LifePath] search record2: {:?}", record2);

let d1 = (record1[0].parse::<i32>().unwrap() - timestamp).abs();
let d2 = (record2[0].parse::<i32>().unwrap() - timestamp).abs();
Expand All @@ -90,7 +90,7 @@ impl LocationReaderBase for LocationReaderLiftPath {
let alt_mid = record1[10].parse::<f64>().unwrap() * p1 + record2[10].parse::<f64>().unwrap() * p2;
let confidence_radius_min = record1[5].parse::<f32>().unwrap().min(record2[5].parse::<f32>().unwrap());

debug!("time_mid: {}, max interval: {}", time_mid, self.param.max_interval);
debug!("[LifePath] time_mid: {}, max interval: {}", time_mid, self.param.max_interval);

if (time_mid - timestamp).abs() > self.param.max_interval as i32 {
return None;
Expand Down
2 changes: 1 addition & 1 deletion src/location_reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub struct LocationReaderParam {
pub max_interval: u32,
}

#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct LocationReaderResult {
pub lat: f64,
pub lon: f64,
Expand Down
4 changes: 2 additions & 2 deletions src/util/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use simple_log::log::debug;
pub fn read_dir_files(dir: &Path, full_path: bool, recursive: bool) -> io::Result<Vec<String>> {
let exts = vec!["jpg", "jpeg", "png", "cr3", "dng", "heic", "RW2", "raw", "raf", "arw", "arq", "nef", "nrw"];

debug!("Reading dir: {:?}", dir);
debug!("[Util readDir] Reading dir: {:?}", dir);

let mut files = Vec::new();

Expand All @@ -24,7 +24,7 @@ pub fn read_dir_files(dir: &Path, full_path: bool, recursive: bool) -> io::Resul
}
let ext = path.extension().unwrap_or("".to_owned().as_ref()).to_string_lossy().to_lowercase();
if !exts.contains(&ext.as_ref()) {
debug!("Skipping file: {:?}", path);
debug!("[Util readDir] Skipping file: {:?}", path);
continue;
}

Expand Down
Loading

0 comments on commit 85b47e1

Please sign in to comment.