1use crate::error::{Error, Result};
2use proj4rs::proj::Proj;
3use std::collections::HashMap;
4use std::sync::OnceLock;
5
6static PROJ_STRS: &[(&str, &str); 20] = &[
7 ("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"),
8 (
9 "公共座標1系", "+proj=tmerc +lat_0=33 +lon_0=129.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
11 ),
12 (
13 "公共座標2系", "+proj=tmerc +lat_0=33 +lon_0=131 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
15 ),
16 (
17 "公共座標3系", "+proj=tmerc +lat_0=36 +lon_0=132.166666666667 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
19 ),
20 (
21 "公共座標4系", "+proj=tmerc +lat_0=33 +lon_0=133.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
23 ),
24 (
25 "公共座標5系", "+proj=tmerc +lat_0=36 +lon_0=134.333333333333 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
27 ),
28 (
29 "公共座標6系", "+proj=tmerc +lat_0=36 +lon_0=136 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
31 ),
32 (
33 "公共座標7系", "+proj=tmerc +lat_0=36 +lon_0=137.166666666667 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
35 ),
36 (
37 "公共座標8系", "+proj=tmerc +lat_0=36 +lon_0=138.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
39 ),
40 (
41 "公共座標9系", "+proj=tmerc +lat_0=36 +lon_0=139.833333333333 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
43 ),
44 (
45 "公共座標10系", "+proj=tmerc +lat_0=40 +lon_0=140.833333333333 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
47 ),
48 (
49 "公共座標11系", "+proj=tmerc +lat_0=44 +lon_0=140.25 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
51 ),
52 (
53 "公共座標12系", "+proj=tmerc +lat_0=44 +lon_0=142.25 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
55 ),
56 (
57 "公共座標13系", "+proj=tmerc +lat_0=44 +lon_0=144.25 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
59 ),
60 (
61 "公共座標14系", "+proj=tmerc +lat_0=26 +lon_0=142 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
63 ),
64 (
65 "公共座標15系", "+proj=tmerc +lat_0=26 +lon_0=127.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
67 ),
68 (
69 "公共座標16系", "+proj=tmerc +lat_0=26 +lon_0=124 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
71 ),
72 (
73 "公共座標17系", "+proj=tmerc +lat_0=26 +lon_0=131 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
75 ),
76 (
77 "公共座標18系", "+proj=tmerc +lat_0=20 +lon_0=136 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
79 ),
80 (
81 "公共座標19系", "+proj=tmerc +lat_0=26 +lon_0=154 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
83 ),
84];
85
86static PROJ_CACHE: OnceLock<HashMap<&'static str, OnceLock<Proj>>> = OnceLock::new();
87
88pub fn get_proj(name: &str) -> Result<Option<&'static Proj>> {
89 if name == "任意座標系" {
90 return Ok(None);
91 }
92 let definition = PROJ_STRS
93 .iter()
94 .find(|(n, _)| *n == name)
95 .map(|(_, s)| *s)
96 .ok_or_else(|| Error::UnsupportedCrs(name.to_string()))?;
97
98 let cache = PROJ_CACHE.get_or_init(|| {
99 let mut map = HashMap::with_capacity(PROJ_STRS.len());
100 for (name, _) in PROJ_STRS.iter() {
101 map.insert(*name, OnceLock::new());
102 }
103 map
104 });
105
106 let cell = cache
107 .get(name)
108 .ok_or_else(|| Error::UnsupportedCrs(name.to_string()))?;
109 let proj = cell.get_or_init(|| {
110 Proj::from_proj_string(definition)
111 .unwrap_or_else(|err| panic!("invalid PROJ definition for {name}: {err:?}"))
112 });
113 Ok(Some(proj))
114}
115
116pub fn get_xml_namespace(prefix: Option<&str>) -> Option<&'static str> {
117 match prefix {
118 None => Some("http://www.moj.go.jp/MINJI/tizuxml"),
119 Some("zmn") => Some("http://www.moj.go.jp/MINJI/tizuzumen"),
120 Some("xsi") => Some("http://www.w3.org/2001/XMLSchema-instance"),
121 _ => None,
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::get_proj;
128 use std::ptr;
129
130 #[test]
131 fn proj_cache_test() {
132 let wgs84 = get_proj("WGS84")
133 .expect("WGS84 lookup should succeed")
134 .expect("WGS84 should not map to None");
135 let wgs84_cached = get_proj("WGS84")
136 .expect("WGS84 cached lookup should succeed")
137 .expect("WGS84 cached lookup should not map to None");
138 assert!(
139 ptr::eq(wgs84, wgs84_cached),
140 "WGS84 should return the same cached Proj instance"
141 );
142
143 let public_zone_1 = get_proj("公共座標1系")
144 .expect("公共座標1系 lookup should succeed")
145 .expect("公共座標1系 should not map to None");
146 let public_zone_1_cached = get_proj("公共座標1系")
147 .expect("公共座標1系 cached lookup should succeed")
148 .expect("公共座標1系 cached lookup should not map to None");
149 assert!(
150 ptr::eq(public_zone_1, public_zone_1_cached),
151 "公共座標1系 should return the same cached Proj instance"
152 );
153
154 assert!(
155 !ptr::eq(wgs84, public_zone_1),
156 "Distinct CRS names should resolve to distinct Proj instances"
157 );
158 }
159}