Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1

This commit is contained in:
2026-10-19 08:34:03 +09:00
21 changed files with 5738 additions and 0 deletions
@@ -0,0 +1 @@
{"project_id":"dad81824-4bb7-41df-ad9c-95c870c2a0dc","route_id":370,"filter_key":"classification","method":"dtm","smooth":true,"crs_epsg":5176,"road_type":"main","defaults":{"station_interval_m":20.0,"cross_half_width_m":20.0,"cross_sample_interval_m":0.5,"long_sample_interval_m":1.0,"vertical_exaggeration":1.0},"standard_cross_section":{"soil":{"road_width_m":3.0,"shoulder_left_m":0.5,"shoulder_right_m":0.5,"ditch":{"top_width_m":0.9,"bottom_width_m":0.3,"depth_m":0.3},"cross_slope_pct":{"min":3.0,"max":5.0},"fill_slope_ratio":1.2,"cut_slope_ratio":1.0},"rock":{"road_width_m":3.0,"shoulder_left_m":0.5,"shoulder_right_m":0.5,"ditch":{"top_width_m":0.69,"bottom_width_m":0.3,"depth_m":0.3},"ditch_l_type":{"width_m":0.5,"depth_m":0.1},"cross_slope_pct":{"min":3.0,"max":3.0},"fill_slope_ratio":1.2,"cut_slope_ratio":0.4},"paved":{"road_width_m":3.0,"shoulder_left_m":0.5,"shoulder_right_m":0.5,"ditch":{"top_width_m":0.9,"bottom_width_m":0.3,"depth_m":0.3},"cross_slope_pct":{"min":1.5,"max":2.0},"fill_slope_ratio":1.2,"cut_slope_ratio":1.0,"pavement_thickness_m":0.2}},"stored_standard_cross_section":null,"rock_boundary_default_offset_m":-0.5,"rock_boundary_step_m":0.1,"earthwork_conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"haul_equipment_limits":[{"key":"free_haul","max_distance_m":20.0},{"key":"dozer","max_distance_m":60.0},{"key":"dump_truck","max_distance_m":null}],"natural_spoil_min_ground_slope":0.6666666666666666}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
{"pipe_points":[{"chainage_m":351.0,"source":"stream","id":"p35100","options":{"pipe_kind":"파형강관","pipe_diameter_mm":"1000","inlet_type":"집수정","inlet_basin_length_m":2,"inlet_basin_before_m":1,"inlet_basin_after_m":1,"inlet_revet_form":"돌쌓기(찰)","inlet_revet_length_m":10,"inlet_revet_before_m":5,"inlet_revet_after_m":5,"outlet_type":"기슭막이","outlet_revet_form":"돌쌓기(찰)","outlet_revet_length_m":10,"outlet_revet_before_m":5,"outlet_revet_after_m":5,"inlet_structure":"L","inlet_revet_height_m":1.7,"outlet_revet_height_m":1.5,"inlet_basin_height_m":1.5},"lonlat":[129.0969624,36.8109679],"x":208597.117,"y":367731.865}]}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"status":"success","chainages":[]}
@@ -0,0 +1,33 @@
{
"route_signature": "181-c94cb897d6ed0270",
"points": [
{
"chainage_m": 351.0,
"source": "stream",
"id": "p35100",
"options": {
"pipe_kind": "파형강관",
"pipe_diameter_mm": "1000",
"inlet_type": "집수정",
"inlet_basin_length_m": 2,
"inlet_basin_before_m": 1,
"inlet_basin_after_m": 1,
"inlet_revet_form": "돌쌓기(찰)",
"inlet_revet_length_m": 10,
"inlet_revet_before_m": 5,
"inlet_revet_after_m": 5,
"outlet_type": "기슭막이",
"outlet_revet_form": "돌쌓기(찰)",
"outlet_revet_length_m": 10,
"outlet_revet_before_m": 5,
"outlet_revet_after_m": 5,
"inlet_structure": "L",
"inlet_revet_height_m": 1.7,
"outlet_revet_height_m": 1.5,
"inlet_basin_height_m": 1.5
},
"x": 208597.117,
"y": 367731.865
}
]
}
@@ -0,0 +1,182 @@
sequence,x,y
0,208403.2001,367920.7514
1,208406.4928,367842.927
2,208406.5836,367841.8768
3,208406.7662,367840.8386
4,208407.0392,367839.8205
5,208407.4004,367838.8302
6,208426.7855,367792.1712
7,208427.2313,367791.2183
8,208427.7588,367790.3082
9,208428.364,367789.4478
10,208429.0423,367788.6436
11,208429.7883,367787.902
12,208430.5965,367787.2286
13,208431.4605,367786.6285
14,208432.3738,367786.1064
15,208433.3292,367785.6663
16,208434.3196,367785.3115
17,208435.3372,367785.0449
18,208436.3743,367784.8684
19,208437.4228,367784.7834
20,208438.4747,367784.7906
21,208468.2344,367786.2993
22,208473.877,367786.9659
23,208505.117,367792.7962
24,208510.3423,367794.063
25,208515.4031,367795.8789
26,208527.5021,367800.9636
27,208528.9177,367801.4569
28,208530.3839,367801.7699
29,208545.3106,367803.9926
30,208546.4412,367804.1065
31,208547.5774,367804.113
32,208548.7092,367804.012
33,208549.8264,367803.8044
34,208550.9189,367803.4921
35,208551.977,367803.0778
36,208562.6578,367798.3011
37,208563.629,367797.8128
38,208564.5521,367797.2387
39,208565.4194,367796.5833
40,208566.2238,367795.8521
41,208566.9588,367795.0512
42,208567.6183,367794.187
43,208568.1969,367793.2666
44,208568.6898,367792.2977
45,208569.0929,367791.2882
46,208580.1819,367759.4003
47,208580.6952,367758.1545
48,208581.345,367756.9741
49,208596.5507,367732.6861
50,208597.1476,367731.8196
51,208597.8181,367731.0088
52,208598.557,367730.2598
53,208599.3587,367729.5784
54,208600.217,367728.9698
55,208601.1253,367728.4388
56,208602.0767,367727.9894
57,208603.0637,367727.625
58,208604.0789,367727.3485
59,208605.1144,367727.162
60,208606.1622,367727.0669
61,208607.2144,367727.0639
62,208608.2627,367727.1532
63,208609.2993,367727.3339
64,208610.316,367727.6047
65,208611.305,367727.9635
66,208612.2589,367728.4076
67,208613.1701,367728.9336
68,208614.0318,367729.5373
69,208636.9831,367747.167
70,208637.7986,367747.8532
71,208638.5501,367748.609
72,208663.7219,367776.2823
73,208664.6721,367777.4703
74,208665.4644,367778.7689
75,208682.8222,367811.7566
76,208683.4357,367812.7928
77,208684.15,367813.7623
78,208692.1195,367823.514
79,208699.2384,367833.6567
80,208704.0665,367841.7335
81,208704.6739,367842.6512
82,208705.3628,367843.5095
83,208706.1275,367844.3009
84,208706.9614,367845.0191
85,208707.8576,367845.6578
86,208708.8086,367846.2117
87,208709.8063,367846.6761
88,208710.8423,367847.0472
89,208716.1671,367848.6831
90,208721.0309,367849.9023
91,208725.9956,367850.6052
92,208731.0066,367850.7841
93,208736.0087,367850.4371
94,208781.4797,367844.8739
95,208782.6655,367844.7882
96,208783.854,367844.8202
97,208785.0334,367844.9697
98,208786.1923,367845.2351
99,208787.3193,367845.614
100,208788.4032,367846.1024
101,208789.4335,367846.6958
102,208809.7708,367859.7909
103,208816.0718,367864.3322
104,208821.8631,367869.5079
105,208827.081,367875.2613
106,208840.6889,367891.9449
107,208841.3869,367892.7282
108,208842.1508,367893.4475
109,208842.9746,367894.0974
110,208843.852,367894.6728
111,208844.7763,367895.1694
112,208845.7404,367895.5833
113,208846.737,367895.9115
114,208847.7585,367896.1513
115,208848.797,367896.3009
116,208849.8446,367896.3593
117,208850.8933,367896.326
118,208851.9351,367896.2011
119,208852.962,367895.9858
120,208899.1319,367884.1744
121,208900.2135,367883.9502
122,208901.3112,367883.8264
123,208902.4156,367883.804
124,208903.5174,367883.8833
125,208904.6072,367884.0636
126,208905.6759,367884.3433
127,208906.7143,367884.7201
128,208907.7136,367885.1908
129,208908.6654,367885.7514
130,208909.5617,367886.3972
131,208934.2094,367905.9409
132,208935.8546,367907.1026
133,208937.6185,367908.0747
134,208939.4793,367908.8451
135,208966.8587,367918.4372
136,208967.8753,367918.7436
137,208968.9151,367918.9589
138,208969.9698,367919.0815
139,208971.0312,367919.1103
140,208972.091,367919.0452
141,208973.1409,367918.8867
142,208974.1727,367918.636
143,208975.1783,367918.2951
144,208976.1498,367917.8666
145,208977.0797,367917.3539
146,208977.9605,367916.7611
147,208978.7856,367916.0926
148,208979.5483,367915.3539
149,208980.2427,367914.5506
150,208980.8634,367913.6891
151,208995.3496,367891.5822
152,208995.92,367890.616
153,208996.3977,367889.6007
154,208996.7785,367888.5453
155,208997.059,367887.4589
156,208997.2368,367886.3511
157,208997.3104,367885.2315
158,208997.2791,367884.1099
159,208997.1432,367882.9962
160,208996.9038,367881.9
161,208996.5632,367880.8309
162,208987.32,367855.9177
163,208986.5266,367853.4357
164,208985.9642,367850.8915
165,208985.6375,367848.3063
166,208985.5491,367845.7021
167,208985.6999,367843.1008
168,208986.0885,367840.5242
169,208991.8532,367811.4317
170,208992.1634,367810.2078
171,208992.6005,367809.0232
172,208993.1597,367807.8912
173,208993.8347,367806.8241
174,209025.4112,367762.2823
175,209026.0307,367761.313
176,209026.5546,367760.2889
177,209026.978,367759.2194
178,209027.297,367758.1142
179,209027.5088,367756.9836
180,209038.0798,367680.426
1 sequence x y
2 0 208403.2001 367920.7514
3 1 208406.4928 367842.927
4 2 208406.5836 367841.8768
5 3 208406.7662 367840.8386
6 4 208407.0392 367839.8205
7 5 208407.4004 367838.8302
8 6 208426.7855 367792.1712
9 7 208427.2313 367791.2183
10 8 208427.7588 367790.3082
11 9 208428.364 367789.4478
12 10 208429.0423 367788.6436
13 11 208429.7883 367787.902
14 12 208430.5965 367787.2286
15 13 208431.4605 367786.6285
16 14 208432.3738 367786.1064
17 15 208433.3292 367785.6663
18 16 208434.3196 367785.3115
19 17 208435.3372 367785.0449
20 18 208436.3743 367784.8684
21 19 208437.4228 367784.7834
22 20 208438.4747 367784.7906
23 21 208468.2344 367786.2993
24 22 208473.877 367786.9659
25 23 208505.117 367792.7962
26 24 208510.3423 367794.063
27 25 208515.4031 367795.8789
28 26 208527.5021 367800.9636
29 27 208528.9177 367801.4569
30 28 208530.3839 367801.7699
31 29 208545.3106 367803.9926
32 30 208546.4412 367804.1065
33 31 208547.5774 367804.113
34 32 208548.7092 367804.012
35 33 208549.8264 367803.8044
36 34 208550.9189 367803.4921
37 35 208551.977 367803.0778
38 36 208562.6578 367798.3011
39 37 208563.629 367797.8128
40 38 208564.5521 367797.2387
41 39 208565.4194 367796.5833
42 40 208566.2238 367795.8521
43 41 208566.9588 367795.0512
44 42 208567.6183 367794.187
45 43 208568.1969 367793.2666
46 44 208568.6898 367792.2977
47 45 208569.0929 367791.2882
48 46 208580.1819 367759.4003
49 47 208580.6952 367758.1545
50 48 208581.345 367756.9741
51 49 208596.5507 367732.6861
52 50 208597.1476 367731.8196
53 51 208597.8181 367731.0088
54 52 208598.557 367730.2598
55 53 208599.3587 367729.5784
56 54 208600.217 367728.9698
57 55 208601.1253 367728.4388
58 56 208602.0767 367727.9894
59 57 208603.0637 367727.625
60 58 208604.0789 367727.3485
61 59 208605.1144 367727.162
62 60 208606.1622 367727.0669
63 61 208607.2144 367727.0639
64 62 208608.2627 367727.1532
65 63 208609.2993 367727.3339
66 64 208610.316 367727.6047
67 65 208611.305 367727.9635
68 66 208612.2589 367728.4076
69 67 208613.1701 367728.9336
70 68 208614.0318 367729.5373
71 69 208636.9831 367747.167
72 70 208637.7986 367747.8532
73 71 208638.5501 367748.609
74 72 208663.7219 367776.2823
75 73 208664.6721 367777.4703
76 74 208665.4644 367778.7689
77 75 208682.8222 367811.7566
78 76 208683.4357 367812.7928
79 77 208684.15 367813.7623
80 78 208692.1195 367823.514
81 79 208699.2384 367833.6567
82 80 208704.0665 367841.7335
83 81 208704.6739 367842.6512
84 82 208705.3628 367843.5095
85 83 208706.1275 367844.3009
86 84 208706.9614 367845.0191
87 85 208707.8576 367845.6578
88 86 208708.8086 367846.2117
89 87 208709.8063 367846.6761
90 88 208710.8423 367847.0472
91 89 208716.1671 367848.6831
92 90 208721.0309 367849.9023
93 91 208725.9956 367850.6052
94 92 208731.0066 367850.7841
95 93 208736.0087 367850.4371
96 94 208781.4797 367844.8739
97 95 208782.6655 367844.7882
98 96 208783.854 367844.8202
99 97 208785.0334 367844.9697
100 98 208786.1923 367845.2351
101 99 208787.3193 367845.614
102 100 208788.4032 367846.1024
103 101 208789.4335 367846.6958
104 102 208809.7708 367859.7909
105 103 208816.0718 367864.3322
106 104 208821.8631 367869.5079
107 105 208827.081 367875.2613
108 106 208840.6889 367891.9449
109 107 208841.3869 367892.7282
110 108 208842.1508 367893.4475
111 109 208842.9746 367894.0974
112 110 208843.852 367894.6728
113 111 208844.7763 367895.1694
114 112 208845.7404 367895.5833
115 113 208846.737 367895.9115
116 114 208847.7585 367896.1513
117 115 208848.797 367896.3009
118 116 208849.8446 367896.3593
119 117 208850.8933 367896.326
120 118 208851.9351 367896.2011
121 119 208852.962 367895.9858
122 120 208899.1319 367884.1744
123 121 208900.2135 367883.9502
124 122 208901.3112 367883.8264
125 123 208902.4156 367883.804
126 124 208903.5174 367883.8833
127 125 208904.6072 367884.0636
128 126 208905.6759 367884.3433
129 127 208906.7143 367884.7201
130 128 208907.7136 367885.1908
131 129 208908.6654 367885.7514
132 130 208909.5617 367886.3972
133 131 208934.2094 367905.9409
134 132 208935.8546 367907.1026
135 133 208937.6185 367908.0747
136 134 208939.4793 367908.8451
137 135 208966.8587 367918.4372
138 136 208967.8753 367918.7436
139 137 208968.9151 367918.9589
140 138 208969.9698 367919.0815
141 139 208971.0312 367919.1103
142 140 208972.091 367919.0452
143 141 208973.1409 367918.8867
144 142 208974.1727 367918.636
145 143 208975.1783 367918.2951
146 144 208976.1498 367917.8666
147 145 208977.0797 367917.3539
148 146 208977.9605 367916.7611
149 147 208978.7856 367916.0926
150 148 208979.5483 367915.3539
151 149 208980.2427 367914.5506
152 150 208980.8634 367913.6891
153 151 208995.3496 367891.5822
154 152 208995.92 367890.616
155 153 208996.3977 367889.6007
156 154 208996.7785 367888.5453
157 155 208997.059 367887.4589
158 156 208997.2368 367886.3511
159 157 208997.3104 367885.2315
160 158 208997.2791 367884.1099
161 159 208997.1432 367882.9962
162 160 208996.9038 367881.9
163 161 208996.5632 367880.8309
164 162 208987.32 367855.9177
165 163 208986.5266 367853.4357
166 164 208985.9642 367850.8915
167 165 208985.6375 367848.3063
168 166 208985.5491 367845.7021
169 167 208985.6999 367843.1008
170 168 208986.0885 367840.5242
171 169 208991.8532 367811.4317
172 170 208992.1634 367810.2078
173 171 208992.6005 367809.0232
174 172 208993.1597 367807.8912
175 173 208993.8347 367806.8241
176 174 209025.4112 367762.2823
177 175 209026.0307 367761.313
178 176 209026.5546 367760.2889
179 177 209026.978 367759.2194
180 178 209027.297 367758.1142
181 179 209027.5088 367756.9836
182 180 209038.0798 367680.426
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
sequence,x,y
0,208403.2001,367920.7514
1,208406.5829,367840.798
2,208430.0297,367784.3624
3,208471.078,367786.4435
4,208510.4251,367793.7869
5,208528.8923,367801.5478
6,208548.7774,367804.5088
7,208567.3902,367796.1847
8,208580.6266,367758.1216
9,208603.5586,367721.4924
10,208637.8308,367747.8182
11,208664.7517,367777.4145
12,208683.3851,367812.8263
13,208696.0508,367828.3244
14,208706.4332,367845.6926
15,208725.9011,367851.6737
16,208785.7859,367844.3471
17,208819.6524,367866.1537
18,208845.5346,367897.8859
19,208904.898,367882.6993
20,208936.6,367907.8364
21,208975.7182,367921.541
22,208998.6713,367886.513
23,208984.5289,367848.395
24,208992.3516,367808.9162
25,209027.1076,367759.8893
26,209038.0798,367680.426
1 sequence x y
2 0 208403.2001 367920.7514
3 1 208406.5829 367840.798
4 2 208430.0297 367784.3624
5 3 208471.078 367786.4435
6 4 208510.4251 367793.7869
7 5 208528.8923 367801.5478
8 6 208548.7774 367804.5088
9 7 208567.3902 367796.1847
10 8 208580.6266 367758.1216
11 9 208603.5586 367721.4924
12 10 208637.8308 367747.8182
13 11 208664.7517 367777.4145
14 12 208683.3851 367812.8263
15 13 208696.0508 367828.3244
16 14 208706.4332 367845.6926
17 15 208725.9011 367851.6737
18 16 208785.7859 367844.3471
19 17 208819.6524 367866.1537
20 18 208845.5346 367897.8859
21 19 208904.898 367882.6993
22 20 208936.6 367907.8364
23 21 208975.7182 367921.541
24 22 208998.6713 367886.513
25 23 208984.5289 367848.395
26 24 208992.3516 367808.9162
27 25 209027.1076 367759.8893
28 26 209038.0798 367680.426
@@ -0,0 +1,914 @@
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[
208403.2,
367920.751,
879.138
],
[
208406.493,
367842.927,
864.405
],
[
208406.584,
367841.877,
864.787
],
[
208406.766,
367840.839,
864.597
],
[
208407.039,
367839.82,
864.415
],
[
208407.4,
367838.83,
864.372
],
[
208426.785,
367792.171,
858.919
],
[
208427.231,
367791.218,
859.26
],
[
208427.759,
367790.308,
859.48
],
[
208428.364,
367789.448,
859.322
],
[
208429.042,
367788.644,
859.34
],
[
208429.788,
367787.902,
859.184
],
[
208430.597,
367787.229,
859.096
],
[
208431.46,
367786.628,
858.932
],
[
208432.374,
367786.106,
858.791
],
[
208433.329,
367785.666,
858.435
],
[
208434.32,
367785.312,
858.212
],
[
208435.337,
367785.045,
858.089
],
[
208436.374,
367784.868,
857.432
],
[
208437.423,
367784.783,
857.18
],
[
208438.475,
367784.791,
856.833
],
[
208468.234,
367786.299,
849.434
],
[
208473.877,
367786.966,
849.2
],
[
208505.117,
367792.796,
841.946
],
[
208510.342,
367794.063,
841.338
],
[
208515.403,
367795.879,
840.214
],
[
208527.502,
367800.964,
838.315
],
[
208528.918,
367801.457,
837.999
],
[
208530.384,
367801.77,
838.144
],
[
208545.311,
367803.993,
838.552
],
[
208546.441,
367804.106,
838.299
],
[
208547.577,
367804.113,
837.705
],
[
208548.709,
367804.012,
837.398
],
[
208549.826,
367803.804,
836.911
],
[
208550.919,
367803.492,
836.234
],
[
208551.977,
367803.078,
835.66
],
[
208562.658,
367798.301,
830.929
],
[
208563.629,
367797.813,
830.249
],
[
208564.552,
367797.239,
829.333
],
[
208565.419,
367796.583,
828.839
],
[
208566.224,
367795.852,
828.786
],
[
208566.959,
367795.051,
828.499
],
[
208567.618,
367794.187,
828.406
],
[
208568.197,
367793.267,
828.319
],
[
208568.69,
367792.298,
828.093
],
[
208569.093,
367791.288,
829.541
],
[
208580.182,
367759.4,
831.257
],
[
208580.695,
367758.155,
832.034
],
[
208581.345,
367756.974,
832.16
],
[
208596.551,
367732.686,
834.536
],
[
208597.148,
367731.82,
834.892
],
[
208597.818,
367731.009,
835.06
],
[
208598.557,
367730.26,
835.092
],
[
208599.359,
367729.578,
835.243
],
[
208600.217,
367728.97,
835.762
],
[
208601.125,
367728.439,
835.842
],
[
208602.077,
367727.989,
835.647
],
[
208603.064,
367727.625,
835.296
],
[
208604.079,
367727.349,
835.329
],
[
208605.114,
367727.162,
835.357
],
[
208606.162,
367727.067,
835.683
],
[
208607.214,
367727.064,
836.126
],
[
208608.263,
367727.153,
836.12
],
[
208609.299,
367727.334,
836.055
],
[
208610.316,
367727.605,
836.457
],
[
208611.305,
367727.964,
836.862
],
[
208612.259,
367728.408,
836.6
],
[
208613.17,
367728.934,
836.145
],
[
208614.032,
367729.537,
835.925
],
[
208636.983,
367747.167,
834.412
],
[
208637.799,
367747.853,
834.377
],
[
208638.55,
367748.609,
834.275
],
[
208663.722,
367776.282,
840.491
],
[
208664.672,
367777.47,
840.779
],
[
208665.464,
367778.769,
841.052
],
[
208682.822,
367811.757,
840.601
],
[
208683.436,
367812.793,
840.136
],
[
208684.15,
367813.762,
840.401
],
[
208692.12,
367823.514,
841.362
],
[
208699.238,
367833.657,
842.384
],
[
208704.066,
367841.733,
842.267
],
[
208704.674,
367842.651,
841.992
],
[
208705.363,
367843.509,
842.264
],
[
208706.128,
367844.301,
842.644
],
[
208706.961,
367845.019,
842.988
],
[
208707.858,
367845.658,
842.964
],
[
208708.809,
367846.212,
843.198
],
[
208709.806,
367846.676,
843.236
],
[
208710.842,
367847.047,
842.565
],
[
208716.167,
367848.683,
839.588
],
[
208721.031,
367849.902,
837.138
],
[
208725.996,
367850.605,
834.492
],
[
208731.007,
367850.784,
832.855
],
[
208736.009,
367850.437,
832.177
],
[
208781.48,
367844.874,
836.645
],
[
208782.666,
367844.788,
837.311
],
[
208783.854,
367844.82,
837.844
],
[
208785.033,
367844.97,
838.022
],
[
208786.192,
367845.235,
837.994
],
[
208787.319,
367845.614,
837.882
],
[
208788.403,
367846.102,
837.728
],
[
208789.434,
367846.696,
837.716
],
[
208809.771,
367859.791,
836.452
],
[
208816.072,
367864.332,
837.15
],
[
208821.863,
367869.508,
837.11
],
[
208827.081,
367875.261,
835.924
],
[
208840.689,
367891.945,
840.651
],
[
208841.387,
367892.728,
840.762
],
[
208842.151,
367893.448,
840.867
],
[
208842.975,
367894.097,
841.553
],
[
208843.852,
367894.673,
842.473
],
[
208844.776,
367895.169,
843.279
],
[
208845.74,
367895.583,
843.863
],
[
208846.737,
367895.911,
844.457
],
[
208847.758,
367896.151,
844.415
],
[
208848.797,
367896.301,
842.684
],
[
208849.845,
367896.359,
841.776
],
[
208850.893,
367896.326,
841.129
],
[
208851.935,
367896.201,
840.86
],
[
208852.962,
367895.986,
840.151
],
[
208899.132,
367884.174,
831.066
],
[
208900.214,
367883.95,
831.285
],
[
208901.311,
367883.826,
831.501
],
[
208902.416,
367883.804,
831.685
],
[
208903.517,
367883.883,
832.09
],
[
208904.607,
367884.064,
832.134
],
[
208905.676,
367884.343,
832.166
],
[
208906.714,
367884.72,
831.968
],
[
208907.714,
367885.191,
831.463
],
[
208908.665,
367885.751,
830.814
],
[
208909.562,
367886.397,
831.28
],
[
208934.209,
367905.941,
835.37
],
[
208935.855,
367907.103,
835.855
],
[
208937.619,
367908.075,
836.345
],
[
208939.479,
367908.845,
837.618
],
[
208966.859,
367918.437,
839.674
],
[
208967.875,
367918.744,
839.231
],
[
208968.915,
367918.959,
838.481
],
[
208969.97,
367919.081,
838.152
],
[
208971.031,
367919.11,
837.728
],
[
208972.091,
367919.045,
837.351
],
[
208973.141,
367918.887,
837.032
],
[
208974.173,
367918.636,
836.809
],
[
208975.178,
367918.295,
836.747
],
[
208976.15,
367917.867,
836.272
],
[
208977.08,
367917.354,
835.905
],
[
208977.96,
367916.761,
835.644
],
[
208978.786,
367916.093,
835.451
],
[
208979.548,
367915.354,
835.276
],
[
208980.243,
367914.551,
835.053
],
[
208980.863,
367913.689,
834.763
],
[
208995.35,
367891.582,
835.441
],
[
208995.92,
367890.616,
835.898
],
[
208996.398,
367889.601,
836.402
],
[
208996.778,
367888.545,
836.989
],
[
208997.059,
367887.459,
837.416
],
[
208997.237,
367886.351,
837.913
],
[
208997.31,
367885.231,
838.623
],
[
208997.279,
367884.11,
839.509
],
[
208997.143,
367882.996,
840.394
],
[
208996.904,
367881.9,
840.934
],
[
208996.563,
367880.831,
841.838
],
[
208987.32,
367855.918,
839.561
],
[
208986.527,
367853.436,
839.032
],
[
208985.964,
367850.892,
838.242
],
[
208985.638,
367848.306,
837.704
],
[
208985.549,
367845.702,
836.2
],
[
208985.7,
367843.101,
835.884
],
[
208986.089,
367840.524,
835.409
],
[
208991.853,
367811.432,
827.88
],
[
208992.163,
367810.208,
828.585
],
[
208992.601,
367809.023,
828.562
],
[
208993.16,
367807.891,
827.848
],
[
208993.835,
367806.824,
827.463
],
[
209025.411,
367762.282,
830.904
],
[
209026.031,
367761.313,
830.97
],
[
209026.555,
367760.289,
831.407
],
[
209026.978,
367759.219,
831.105
],
[
209027.297,
367758.114,
831.941
],
[
209027.509,
367756.984,
831.658
],
[
209038.08,
367680.426,
833.018
]
]
},
"properties": {}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"status":"success","project_id":"dad81824-4bb7-41df-ad9c-95c870c2a0dc","revision":0,"structures":[],"definition_version":null}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"status":"success","project_id":"dad81824-4bb7-41df-ad9c-95c870c2a0dc","sources":["las","sheet"],"design":"las","grid":{"model_id":624,"smooth":true,"signature":"d5004159dc8bf8b6.cf47fc9738101ba1"}}
+154
View File
@@ -0,0 +1,154 @@
/* 472-1 헬퍼 — 브라우저 길 기준값(진짜 단계 함수 · vite ssr 묶음 · helper_472_1_browser_entry.ts).
* node helper_472_1_browser.mjs <묶음.mjs> <고정 자료 폴더> <격자 .bin>
* 페이지 묶음(`design_pipeline_page`)과 같은 단계 붙임 — 저장 노선 · 초안 없음 · 세션 = 메모리 가짜 · fetch 없음 ·
* 격자 = 서버 `dtm_grid_bytes` 가 고정 npz 로 지은 이진. 묶음 출력 → 새 노선 상세(`draftSectionDetail`) → 구조물
* 면적 얹기 → 누가토량. 표준출력 JSON 한 벌(값 꼴 = test_472_1_golden.py `values_of`). */
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
const storage = () => {
const m = new Map();
return {
getItem: (k) => (m.has(k) ? m.get(k) : null),
setItem: (k, v) => m.set(k, String(v)),
removeItem: (k) => m.delete(k),
key: (i) => [...m.keys()][i] ?? null,
get length() {
return m.size;
},
clear: () => m.clear(),
};
};
globalThis.window = globalThis;
globalThis.sessionStorage = storage();
globalThis.localStorage = storage();
const any = new Proxy(function () {}, {
get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : any),
apply: () => any,
construct: () => any,
});
globalThis.document = any;
globalThis.requestAnimationFrame = (run) => setTimeout(run, 0);
globalThis.fetch = () => Promise.reject(new Error("서버 없음"));
const [bundle, dir, gridPath] = process.argv.slice(2);
const m = await import(pathToFileURL(bundle).href);
const read = (name) => JSON.parse(readFileSync(join(dir, name), "utf8"));
const detail = read("detail.json");
const plan = read("plan.json");
const context = read("context.json");
const pipes = read("pipes.json").pipe_points.map(({ lonlat: _l, ...pipe }) => pipe);
const structures = read("structures.json").structures;
const types = read("structure_types.json").types;
const plus = read("plus_stations.json").chainages;
const P = context.project_id;
const R = context.route_id;
const bin = readFileSync(gridPath);
const grid = m.parseDtmGrid(bin.buffer.slice(bin.byteOffset, bin.byteOffset + bin.byteLength));
// 페이지가 열 때 세션에 두는 것 — 표준 · 암 경계 기본 · 저장 상세(design_view_profile · 상세 가게)
localStorage.setItem("frd_current_project_id", P);
m.writeState("std-cross-default", context.standard_cross_section, P);
m.writeStateRaw("rock-boundary-default", String(context.rock_boundary_default_offset_m), P);
m.writeState("section-detail", detail, P, R);
const interval = m.SECTION_STATION_INTERVAL_M;
const enabled = types.filter((type) => type.enabled !== false);
const savedLine = () => plan.planned.map(([x, y]) => [x, y]);
const savedCurves = () =>
(plan.curves ?? []).map(({ start, apex, end, radius_m }) => ({ start, apex, end, radius_m }));
const savedStations = m.buildStations(savedLine(), savedCurves(), pipes, structures, {
intervalM: interval,
types: enabled,
stationSnap: plan.station_snap ?? false,
stationSnapStep: plan.station_snap_step,
});
const crossSource = m.pipelineCrossSource(P, detail, savedStations, R);
const edits = m.readAlignment(detail.longitudinal).edits;
const status = m.shapeStatus(pipes, structures, types);
const bermAt = (list) => {
const spans = m.bermSpansFromStructures(Array.isArray(list) ? list : []);
return (chainageM) => m.bermSpecAt(spans, chainageM);
};
const crossStage = m.createCrossStage({ grid: () => ({ grid }) });
const pipe = m.createDesignPipeline({
stages: {
route: m.createRouteStage({
minRadiusM: () => plan.min_radius_m ?? 0,
savedLine: () => ({ line: savedLine(), curves: savedCurves() }),
}),
follow: m.createFollowStage({
savedLine,
saved: () => ({ pipes, structures, alignment: edits }),
}),
stations: m.createStationsStage({
intervalM: interval,
types: () => enabled,
saved: () => plan,
}),
ground: m.createGroundStage({
grid: () => ({ grid, design: "las" }),
remote: () => Promise.reject(new Error("서버 없음")),
}),
planline: m.createPlanlineStage({
savedBase: (at) => m.plantedBase({ base: null, detail }, at, types),
savedLine,
}),
profile: m.profileStage,
cross: (ctx) =>
crossStage.stage({
...ctx,
inputs: {
...ctx.inputs,
cross: m.crossWithContext(
ctx.inputs.cross,
ctx.out.ground?.stations ?? [],
m.shapeStatus(ctx.inputs.pipes, ctx.inputs.structures, types),
bermAt(ctx.inputs.structures),
),
},
}),
footprint: m.footprintStage,
},
});
const record = await pipe.start({
route: null,
alignment: edits,
cross: crossSource?.input ?? null,
pipes,
structures,
plusStations: plus,
});
await pipe.settled();
if (record.outcome !== "done") throw new Error(`묶음 ${record.outcome}`);
const ground = pipe.output("ground");
const profile = pipe.output("profile");
const shown = m.draftSectionDetail(
detail,
ground,
profile,
pipe.output("cross"),
crossSource?.specs ?? new Map(),
interval,
status,
);
m.applyStructureAreaRows(shown.cross_sections, m.structureAreaRows(shown.cross_sections));
const haul = m.computeMassHaul(
shown.cross_sections,
context.earthwork_conversion,
context.natural_spoil_min_ground_slope ?? undefined,
);
process.stdout.write(
JSON.stringify({
keys: ground.stations.map((station) => station.key),
sections: shown.cross_sections,
mass_haul: haul,
warnings: profile.warnings ?? [],
violations: profile.violations ?? [],
footprint_records: pipe.output("footprint")?.records?.length ?? 0,
skipped: record.skipped,
}),
);
process.exit(0);
@@ -0,0 +1,36 @@
/* 472-1 기준값 시험 묶음 진입점 — 브라우저 길(진짜 단계 함수 · 묶음 · 상세 · 면적 · 누가토량 · vite ssr).
* 짝 = `helper_472_1_browser.mjs` · `test_472_1_golden.py`. */
export { createDesignPipeline } from "../../A00_Common/design/logic/design_pipeline";
export { createStationsStage } from "../../A00_Common/design/logic/design_pipeline_stations";
export { pipelineCrossSource } from "../../A00_Common/design/design_pipeline_cross";
export { plantedBase } from "../../A00_Common/design/design_pipeline_base";
export {
createGroundStage,
createRouteStage,
} from "../../B05_Profile/logic/B05_Profile_Pipeline_RouteGround";
export {
createFollowStage,
createPlanlineStage,
profileStage,
} from "../../B05_Profile/logic/B05_Profile_Pipeline_Grade";
export {
crossWithContext,
draftSectionDetail,
shapeStatus,
} from "../../B05_Profile/logic/B05_Profile_Pipeline_Detail";
export {
buildStations,
SECTION_STATION_INTERVAL_M,
} from "../../B05_Profile/logic/B05_Profile_Sections_Stations";
export { footprintStage } from "../../B05_Profile/logic/B05_Profile_Footprint_Stage";
export { readAlignment } from "../../B05_Profile/logic/B05_Profile_UI_Profile_Data";
export { planElevationAt } from "../../B05_Profile/logic/B05_Profile_UI_Profile_Alignment";
export { createCrossStage } from "../../B06_Section/logic/B06_Section_Cross_Stage";
export {
applyStructureAreaRows,
structureAreaRows,
} from "../../B06_Section/B06_Section_Structure_Layouts";
export { bermSpansFromStructures, bermSpecAt } from "../../B06_Section/B06_Section_UI_Page_Persist";
export { parseDtmGrid } from "../../common_util/common_util_surface_sampler";
export { computeMassHaul } from "../../common_util/common_util_mass_haul";
export { writeState, writeStateRaw } from "../../A00_Common/b_page_state";
+99
View File
@@ -0,0 +1,99 @@
"""472-1 고정 자료 · 값 다시 뜨기(사용자 · 브레인 승인 때만 손으로 돌림 — 시험은 다시 안 뜸).
./venv/Scripts/python.exe resources/tester/helper_472_1_dump.py # 값만 다시
./venv/Scripts/python.exe resources/tester/helper_472_1_dump.py --fetch # 고정 자료도
--fetch = 검증_랩탑메인(`PROJECT`) 노선 하나를 **읽기만**(GET · storage 파일 사본) —
쓰기 · 저장 · 지움 없음.
서버 = 자기 포트(`--base` · 기본 8004) · 로그인 = 메인 폴더 `tmp/aislo_cred.json`(값은 출력 안 함).
"""
from __future__ import annotations
import argparse
import json
import shutil
import sys
import tempfile
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parents[1]))
sys.path.insert(0, str(HERE))
import helper_472_1_paths as paths # noqa: E402
PROJECT = "dad81824-4bb7-41df-ad9c-95c870c2a0dc"
MAIN = Path("C:/Program_coding/임도설계 및 견적자동화 프로그램 개발")
#: storage 사본 — 노선 저장 체인 · [저장] 재계산이 읽는 파일만(노선 · 곡선표 · 관 · 설계 지표면).
STORAGE_FILES = (
"B05_Profile/route/route_main.geojson",
"B05_Profile/route/planned_route_initial.csv",
"B05_Profile/route/planned_route_initial_curves.json",
"B05_Profile/route/planned_route_initial_nodes.csv",
"B04_PreProcess/drainage/edits/pipe_points.json",
paths.NPZ,
)
def fetch(base: str) -> None:
import httpx
cred = json.loads((MAIN / "tmp" / "aislo_cred.json").read_text(encoding="utf-8"))
client = httpx.Client(base_url=f"{base}/api", timeout=120)
login = client.post(
"/auth/login/request", json={"email": cred["email"], "password": cred["password"]}
)
login.raise_for_status()
get = lambda path: client.get(path).raise_for_status().json() # noqa: E731
latest = get(f"/projects/{PROJECT}/route/latest")
route = int(latest["route"]["id"])
out = {
"detail.json": get(f"/projects/{PROJECT}/sections/{route}/detail"),
"summary.json": get(f"/projects/{PROJECT}/sections/{route}"),
"context.json": get(f"/projects/{PROJECT}/sections/context"),
"plan.json": get(f"/projects/{PROJECT}/route/plan"),
"pipes.json": {
"pipe_points": get(f"/projects/{PROJECT}/drainage/pipe-points")["pipe_points"]
},
"structures.json": get(f"/projects/{PROJECT}/route/structures"),
"plus_stations.json": get(f"/projects/{PROJECT}/section/plus-stations"),
"structure_types.json": get("/projects/structure-types"),
"terrain_sources.json": get(f"/projects/{PROJECT}/route/terrain-sources"),
}
assert out["context.json"]["route_id"] == route
paths.FIXTURE.mkdir(parents=True, exist_ok=True)
for name, body in out.items():
(paths.FIXTURE / name).write_text(
json.dumps(body, ensure_ascii=False, separators=(",", ":")), encoding="utf-8"
)
project = next((MAIN / "storage").glob(f"*/*/{PROJECT}"))
for relative in STORAGE_FILES:
target = paths.FIXTURE / "project" / relative # 폴더 이름 storage = .gitignore
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(project / relative, target)
def dump() -> dict:
values: dict = {"project_id": PROJECT}
with tempfile.TemporaryDirectory() as tmp:
for name in paths.PATHS:
work = Path(tmp) / name
work.mkdir()
values[name] = paths.run_path(name, work)
paths.VALUES.write_text(json.dumps(values, ensure_ascii=False, indent=1), encoding="utf-8")
return values
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--fetch", action="store_true")
parser.add_argument("--base", default="http://localhost:8004")
args = parser.parse_args()
if args.fetch:
fetch(args.base)
got = dump()
size = sum(p.stat().st_size for p in paths.FIXTURE.rglob("*") if p.is_file())
print(f"고정 자료 {size / 1e6:.2f} MB")
for name in paths.PATHS:
print(name, got[name]["station_count"], got[name]["final_cumulative_m3"])
+323
View File
@@ -0,0 +1,323 @@
"""472-1 기준값 — 고정 자료(`fixtures/golden_472`) 하나로 길 넷의 값을 뜸(시험 · 값 뜨기 함께 씀).
stored GET 상세(정본 그대로) → 누가토량(서버 Node 묶음 `computeMassHaul`)
server_save [저장] 서버 재계산 — 정본 상세 → `enforce_design_lines` →
`_enforce_stored_designs` → server-calc
server_route 노선 저장 체인(같은 노선 · DB 없이) — `run_section_generation`(고정 geojson · npz)
→ `_carry_profile` 같은 노선 갈래(옛 base_pvi · 편집) → `_apply_alignment_edits` →
측점 열쇠 이월(`carry_cross_inputs`) → `compute_default_designs` → 상세 읽기 →
server_save 와 같은 끝
browser 진짜 단계 함수 묶음(`helper_472_1_browser.mjs` · vite ssr) — 격자 = 같은 npz 의
`dtm_grid_bytes`
값 꼴(`values_of`) — 측점마다 열쇠 · 누가거리(0.01) · x · y(0.001) · 계획고(0.001) · 면적(0.0001) ·
측구 · 층따기 밑수 · 단면 모양 · 누가토량(0.01) + 끝값 · 관 길이 · 종단 경고.
DB · 서버 · 사용자 storage 안 씀(고정 자료 사본만).
"""
from __future__ import annotations
import copy
import json
import shutil
import subprocess
from pathlib import Path
from typing import Any
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
FIXTURE = HERE / "fixtures" / "golden_472"
VALUES = FIXTURE / "golden_472_values.json"
ROUTE = "B05_Profile/route/route_main.geojson"
LONG = "B06_Section/longitudinal/longitudinal.json"
NPZ = "B04_PreProcess/models/dtm_classification_smooth.npz"
SURFACE = ("classification", "dtm", True)
def read(name: str) -> Any:
return json.loads((FIXTURE / name).read_text(encoding="utf-8"))
def stage_project(root: Path) -> Path:
"""고정 storage 사본을 임시 프로젝트 뿌리로 — 체인이 파일을 쓰므로 늘 새 사본."""
shutil.copytree(FIXTURE / "project", root, dirs_exist_ok=True)
return root
# ── 값 꼴 ────────────────────────────────────────────────────────────────────
def _r(value: Any, digits: int) -> float | None:
return round(float(value), digits) if isinstance(value, (int, float)) else None
def values_of(
sections: list[dict[str, Any]],
keys: list[str | None],
mass_haul: dict[str, Any] | None,
warnings: list[str],
violations: list[dict[str, Any]],
) -> dict[str, Any]:
"""상세 측점(누가거리 차례) · 열쇠 · 누가토량 → 비교 값. 누가토량은 같은 측점 id 로 짝."""
rows = sorted(zip(sections, keys, strict=True), key=lambda pair: float(pair[0]["chainage_m"]))
points = {p["station_id"]: p for p in (mass_haul or {}).get("points") or []}
stations = []
for section, key in rows:
design = section.get("design") or {}
point = points.get(section.get("station_id")) or {}
stations.append(
{
"key": key,
"chainage_m": _r(section["chainage_m"], 2),
"x": _r(section.get("center_x"), 3),
"y": _r(section.get("center_y"), 3),
"plan_z": _r(design.get("design_elevation_m"), 3),
"mode": design.get("section_mode"),
"cut_m2": _r(design.get("cut_area_m2"), 4),
"fill_m2": _r(design.get("fill_area_m2"), 4),
"cut_soil_m2": _r(design.get("cut_soil_area_m2"), 4),
"cut_rock_m2": _r(design.get("cut_rock_area_m2"), 4),
"ditch_m2": _r(design.get("ditch_area_m2"), 4),
"bench_m": _r(design.get("bench_cut_length_m"), 4),
"pipe_length_m": _r(design.get("pipe_length_m"), 2),
"cumulative_m3": _r(point.get("cumulative_volume_m3"), 2),
}
)
return {
"station_count": len(stations),
"stations": stations,
"final_cumulative_m3": _r((mass_haul or {}).get("final_cumulative_m3"), 2),
"pipe_lengths": {
s["key"]: s["pipe_length_m"] for s in stations if s["pipe_length_m"] is not None
},
"warnings": sorted(warnings),
"violations": len(violations),
}
def _keys(longitudinal: dict[str, Any], sections: list[dict[str, Any]], root: Path) -> list:
"""서버 측점 열쇠(`station_keys` · 노선 저장 이월이 쓰는 한 벌) — 상세 측점 차례."""
from B05_Profile.B05_Profile_Route_Save_Parts import station_keys
from B05_Profile.B05_Profile_Structures_Repository import load_structures
from common_util.common_util_drainage_pipes import pipe_points_path_in, read_pipe_points_file
pipes = read_pipe_points_file(pipe_points_path_in(root))
_, structures = load_structures(str(root))
interval = float(
(longitudinal.get("profile_alignment") or {}).get("policy", {}).get("station_interval_m")
or 20.0
)
found = station_keys(longitudinal.get("stations") or [], pipes, structures, interval)
by_m = {round(at, 3): key for key, at in found.items()}
return [by_m.get(round(float(s["chainage_m"]), 3)) for s in sections]
# ── 서버 끝(server_save · server_route 공통) ──────────────────────────────────
def _context() -> dict[str, Any]:
"""유토곡선 문맥 — 서버 `_mass_haul_context` + 고정 자료 계수 · 장비 경계(화면이 받는 값)."""
from B06_Section.B06_Section_Server_Calc_Prebuild import _mass_haul_context
context = read("context.json")
limits = [(row["key"], row["max_distance_m"]) for row in context["haul_equipment_limits"]]
return _mass_haul_context(None, context["earthwork_conversion"], limits)
def _save_tail(detail: dict[str, Any], root: Path) -> tuple[list, dict | None]:
"""[저장] 재계산 본문(`_recompute`) — 설계선 · 넓힘 · 보정 → Node 면적 · 누가토량 얹음."""
from B06_Section.B06_Section_Router_Design import enforce_design_lines
from B06_Section.B06_Section_Server_Calc_Prebuild import (
_AREA_KEYS,
_NPM_SCRIPT,
BUNDLE,
_enforce_stored_designs,
)
from common_util.common_util_node_bundle import run_bundle_json
from common_util.common_util_surface_sampler import build_surface_sampler
longitudinal = detail["longitudinal"]
sections = detail["cross_sections"]
enforce_design_lines(longitudinal, sections, root, None)
unclosed = any((s.get("design") or {}).get("slope_unclosed") for s in sections)
sampler = (
build_surface_sampler(root / "B04_PreProcess" / "models", *SURFACE) if unclosed else None
)
_enforce_stored_designs(longitudinal, sections, root, None, sampler)
detail = json.loads(
json.dumps(detail, default=str)
) # 응답 직렬화와 같은 꼴(`model_dump(json)`)
output = run_bundle_json(BUNDLE, _NPM_SCRIPT, {"detail": detail, "context": _context()})
if not isinstance(output, dict):
raise RuntimeError("server-calc 묶음 실패")
sections = detail["cross_sections"]
for row in output.get("areas") or []:
for section in sections:
if abs(float(section["chainage_m"]) - float(row["chainage_m"])) < 1e-6:
design = section.setdefault("design", {})
design.update(
{k: row[k] for k in _AREA_KEYS if isinstance(row.get(k), (int, float))}
)
return sections, output.get("mass_haul")
def server_save(root: Path) -> dict[str, Any]:
detail = copy.deepcopy(read("detail.json"))
alignment = detail["longitudinal"].get("profile_alignment") or {}
keys = _keys(detail["longitudinal"], detail["cross_sections"], root)
sections, haul = _save_tail(detail, root)
return values_of(
sections, keys, haul, alignment.get("warnings") or [], alignment.get("violations") or []
)
def server_route(root: Path) -> dict[str, Any]:
"""노선 저장 체인 — 같은 노선(바뀐 구간 없음) · DB 행 대신 고정 자료(요약 옵션 · 정본 설계)."""
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions
from B05_Profile.B05_Profile_Engine_Grade_Carry import carry_policy
from B05_Profile.B05_Profile_Engine_Sections import run_section_generation
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
from B05_Profile.B05_Profile_Route_Save_Parts import carry_cross_inputs, station_keys
from B05_Profile.B05_Profile_Router import _apply_alignment_edits
from B05_Profile.B05_Profile_Structures_Repository import load_structures
from B06_Section.B06_Section_Engine_Design import ROUTE_CARRY_KEYS
from B06_Section.B06_Section_Router import _read_section_detail
from B06_Section.B06_Section_Router_Design import (
CARRY_INPUT_KEYS,
STRUCTURE_ADJUST_KEYS,
attach_default_designs,
compute_default_designs,
)
from common_util.common_util_drainage_pipes import pipe_points_path_in, read_pipe_points_file
from common_util.common_util_json import atomic_write_json
saved = read("detail.json")
summary = read("summary.json")["longitudinal"]["data"]
options = {k: v for k, v in summary["options"].items() if k != "extra_stations"}
run_section_generation(
root,
ROUTE,
*SURFACE,
options=SectionGenerationOptions(**options),
grade_options=GradeDesignOptions(**summary["grade_options"]),
crs=f"EPSG:{read('context.json')['crs_epsg']}",
)
# 계획고 옮김(`_carry_profile` · 바뀐 구간 없음 = 옛 base_pvi · 편집 통째)
old = saved["longitudinal"]["profile_alignment"]
longitudinal = json.loads((root / LONG).read_text(encoding="utf-8"))
alignment = longitudinal.get("profile_alignment") or {}
line = json.loads((root / ROUTE).read_text(encoding="utf-8"))["geometry"]["coordinates"]
xy = [[float(p[0]), float(p[1])] for p in line]
alignment["policy"] = carry_policy(xy, xy, old, alignment)
alignment["base_pvi"] = old["base_pvi"]
longitudinal["profile_alignment"] = alignment
atomic_write_json(root / LONG, longitudinal)
alignment, _summary, _dropped = _apply_alignment_edits(root, LONG, old.get("edits") or {})
# 횡단 이월(`_carry_cross`) — 측점 열쇠로 정본 사용자 입력 → 새 측점 · 전 측점 새 계획고로 셈
new_long = json.loads((root / LONG).read_text(encoding="utf-8"))
pipes = read_pipe_points_file(pipe_points_path_in(root))
_, structures = load_structures(str(root))
interval = float(options["station_interval_m"])
old_keys = station_keys(saved["longitudinal"]["stations"], pipes, structures, interval)
new_keys = station_keys(new_long.get("stations") or [], pipes, structures, interval)
designs = {
round(float(s["chainage_m"]), 3): s["design"]
for s in saved["cross_sections"]
if isinstance(s.get("design"), dict)
}
inputs, adjusts, _ = carry_cross_inputs(
old_keys, new_keys, designs, (*CARRY_INPUT_KEYS, *ROUTE_CARRY_KEYS), STRUCTURE_ADJUST_KEYS
)
chainages = [float(s["chainage_m"]) for s in new_long.get("stations") or []]
entries = compute_default_designs(root, LONG, chainages, None, inputs)
by_m = {round(at, 3): {**design, **adjusts.get(round(at, 3), {})} for at, design in entries}
# [확정] 상세 읽기(`get_section_detail`) — 행 설계(0.01 m) 얹기 · 없는 측점 기본값
detail = _read_section_detail(root, LONG)
for section in detail["cross_sections"]:
hit = next(
(d for at, d in by_m.items() if abs(at - float(section["chainage_m"])) < 0.01), None
)
if hit is not None:
section["design"] = hit
attach_default_designs(detail["longitudinal"], detail["cross_sections"], root, None)
keys = _keys(detail["longitudinal"], detail["cross_sections"], root)
sections, haul = _save_tail(detail, root)
return values_of(
sections, keys, haul, alignment.get("warnings") or [], alignment.get("violations") or []
)
def stored(root: Path) -> dict[str, Any]:
"""정본 그대로 — GET 상세 면적 · 서버 Node 묶음과 같은 누가토량(화면 배지 값)."""
from B06_Section.B06_Section_Server_Calc_Prebuild import _NPM_SCRIPT, BUNDLE
from common_util.common_util_node_bundle import run_bundle_json
detail = read("detail.json")
alignment = detail["longitudinal"].get("profile_alignment") or {}
keys = _keys(detail["longitudinal"], detail["cross_sections"], root)
output = run_bundle_json(BUNDLE, _NPM_SCRIPT, {"detail": detail, "context": _context()})
haul = (output or {}).get("mass_haul")
return values_of(
detail["cross_sections"],
keys,
haul,
alignment.get("warnings") or [],
alignment.get("violations") or [],
)
# ── 브라우저 길 ──────────────────────────────────────────────────────────────
def browser(work: Path) -> dict[str, Any]:
"""vite ssr 로 진짜 모듈을 묶어 헬퍼를 돌림(`helper_272_detail_memory` 꼴) · 격자 = 고정 npz."""
from common_util.common_util_surface_sampler import dtm_grid_bytes
grid = work / "grid.bin"
grid.write_bytes(dtm_grid_bytes(FIXTURE / "project" / NPZ))
build = subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"),
"build",
"--configLoader",
"runner",
"--ssr",
"../resources/tester/helper_472_1_browser_entry.ts",
"--outDir",
str(work / "b"),
"--logLevel",
"error",
],
cwd=ROOT,
capture_output=True,
text=True,
encoding="utf-8",
timeout=300,
)
if build.returncode:
raise RuntimeError(build.stderr)
bundle = work / "b" / "bundle.mjs" # 조각(assets/)과 같은 폴더
shutil.copy(work / "b" / "helper_472_1_browser_entry.js", bundle)
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HERE / "helper_472_1_browser.mjs"), str(bundle), str(FIXTURE), str(grid)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
)
if proc.returncode:
raise RuntimeError(proc.stderr)
out = json.loads(proc.stdout)
return values_of(
out["sections"], out["keys"], out["mass_haul"], out["warnings"], out["violations"]
)
PATHS = ("stored", "server_save", "server_route", "browser")
def run_path(name: str, work: Path) -> dict[str, Any]:
if name == "browser":
return browser(work)
return globals()[name](stage_project(work / "project"))
+149
View File
@@ -0,0 +1,149 @@
"""PLAN 472-1 (요구 273) — B05 · B06 기준값 시험 · 구조 다시 세움 단계마다의 문.
고정 자료 `fixtures/golden_472/`(검증_랩탑메인 노선 370 · 읽기만 뜸) 하나로 길 넷의 값을 떠
`golden_472_values.json` 과 견줌(값 꼴 · 길 = `helper_472_1_paths.py` 머리).
못박는 것
- 길마다 자기 값과 0 차 — 측점(수 · 열쇠 · 누가거리 · x · y) · 계획고 ·
면적(절 · 성 · 토사 · 암 · 측구) · 층따기 밑수 · 단면 모양 · 누가토량(측점별 · 끝값) ·
관 길이 · 종단 경고 · 기울기 위반 수.
- 두 길 사이 = 「알려진 차」(`KNOWN`)만 · 그 밖 차는 실패 · 안 나타난 알려진 차도 실패
(목록에서 지움 — 472-4 가 짝을 하나 옮길 때마다 한 줄씩 지워 마지막에 빈 목록).
- 화면 참값 — 정본 누가토량 −17,570.70 ㎥ · 측점 57(465 기준값).
이 고정 자료가 안 거치는 짝인데 값이 갈리는 자리 — X1 단면 모양 기본값(설계 없는 새 측점 없음) ·
X3 면적 두 갈래(구조물 = 관 351 하나 · 두 길 다 Node TS 로 셈) · X4 · X6(노선 안 바뀜).
값 다시 뜨기 = `helper_472_1_dump.py`(승인 때만 · 시험은 다시 안 뜸).
./venv/Scripts/python.exe -m pytest resources/tester/test_472_1_golden.py -q
"""
from __future__ import annotations
import json
from dataclasses import dataclass
import helper_472_1_paths as paths
import pytest
pytestmark = pytest.mark.skipif(
not (paths.ROOT / "config" / "node_modules" / "vite").is_dir(),
reason="프론트엔드 의존성 미설치",
)
GOLDEN = json.loads(paths.VALUES.read_text(encoding="utf-8"))
AREAS = ("cut_m2", "fill_m2", "cut_soil_m2", "cut_rock_m2", "ditch_m2")
@dataclass(frozen=True)
class Known:
"""알려진 차 한 줄 — 칸 → 허용 폭(None = 같지 않아도 됨 · 글 칸) · 까닭."""
id: str
fields: dict[str, float | None]
why: str
KNOWN: dict[tuple[str, str], list[Known]] = {
(
"server_save",
"stored",
): [], # [저장] 재계산 = 정본 그대로(사용자 원칙 — 입력 안 바뀌면 값 안 바뀜)
("server_route", "stored"): [
Known(
"R7",
{"fill_m2": 0.00011, "cumulative_m3": 0.011},
"다시 뜬 표본 마지막 자리 — grid:37 성토 1e-4 ㎡ · 누가토량 0.01 ㎥ 넷(끝값 같음)",
),
Known(
"W0",
{"warnings": None},
"정본 종단 경고 = 10-17 저장 때 코드(법정 R · L 미달 46 줄 전) · "
"[저장] 재계산은 종단 경고를 안 씀",
),
],
("browser", "server_route"): [
Known(
"X2",
{
"x": 0.002,
"y": 0.002,
**dict.fromkeys(AREAS, 0.1),
"bench_m": 1.0,
"cumulative_m3": 2.0,
"final_cumulative_m3": 1.0,
},
"입력 선 — 브라우저 /route/plan planned(0.1 mm) · 서버 노선 geojson(1 mm) · "
"측점 자리 ≤ 1 mm → 표본 · 면적 ≤ 0.07 ㎡ · 층따기 밑수 ≤ 0.52 m · "
"누가토량 끝값 0.69 ㎥(−17,570.01 ↔ −17,570.70)",
),
Known(
"W1",
{"warnings": None},
"종단 경고 글 두 벌(X5 · P8) — TS 「340.0m: …」 · 파이썬 「chainage 340.0m: …」 · "
"최대 기울기 총괄 줄은 파이썬만",
),
],
}
@pytest.fixture(scope="module")
def got(tmp_path_factory) -> dict:
out = {}
for name in paths.PATHS:
out[name] = paths.run_path(name, tmp_path_factory.mktemp(name))
return out
def diffs(a: dict, b: dict) -> list[tuple[str, str, object, object]]:
"""(칸, 측점 열쇠, a, b) — 측점은 같은 차례(수가 다르면 측점 수 한 줄)."""
out = []
for field in ("station_count", "final_cumulative_m3", "pipe_lengths", "warnings", "violations"):
if a[field] != b[field]:
out.append((field, "-", a[field], b[field]))
if a["station_count"] == b["station_count"]:
for x, y in zip(a["stations"], b["stations"], strict=True):
out += [(k, x["key"], x[k], y[k]) for k in x if x[k] != y[k]]
return out
def allowed(diff: tuple, known: Known) -> bool:
field, _key, a, b = diff
if field not in known.fields:
return False
width = known.fields[field]
if width is None:
return True
return isinstance(a, (int, float)) and isinstance(b, (int, float)) and abs(a - b) <= width
@pytest.mark.parametrize("name", paths.PATHS)
def test_길마다_기준값_그대로(got, name) -> None:
found = diffs(got[name], GOLDEN[name])
assert not found, f"{name} 기준값과 다름 {len(found)}곳 — 앞 10: {found[:10]}"
@pytest.mark.parametrize("pair", list(KNOWN), ids=lambda pair: f"{pair[0]}-{pair[1]}")
def test_두_길_사이_알려진_차만(got, pair) -> None:
known = KNOWN[pair]
found = diffs(got[pair[0]], got[pair[1]])
stray = [d for d in found if not any(allowed(d, k) for k in known)]
assert not stray, f"알려진 차 밖 {len(stray)}곳 — 앞 10: {stray[:10]}"
gone = [k.id for k in known if not any(allowed(d, k) for d in found)]
assert not gone, f"안 나타난 알려진 차 {gone} — KNOWN 에서 지움"
def test_화면_참값() -> None:
stored = GOLDEN["stored"]
assert stored["final_cumulative_m3"] == -17570.7
assert stored["station_count"] == 57
assert stored["pipe_lengths"] == {"pipe:p35100": 7.9}
def test_고정_자료_크기() -> None:
size = sum(p.stat().st_size for p in paths.FIXTURE.rglob("*") if p.is_file())
assert size < 5e6
@pytest.mark.skip(reason="API 왕복(목록 4.2 ③ 선택) — 472-1 에서 건너뜀")
def test_API_왕복() -> None:
raise AssertionError