From 40a58c2f138e226462ba41b9702e99e68ed73a98 Mon Sep 17 00:00:00 2001 From: Hoa Ben The Nguyen <hbnguye@stud.ntnu.no> Date: Wed, 13 Mar 2024 19:28:44 +0100 Subject: [PATCH] change: working calculation model that sends with sensor and group id, is tested --- .../area_processing.cpython-39.pyc | Bin 1480 -> 1584 bytes .../process_lidar_data.cpython-39.pyc | Bin 3085 -> 3491 bytes server/data_processing/area_processing.py | 23 ++++++++-- server/data_processing/process_lidar_data.py | 40 ++++++++++++------ .../__pycache__/input_new_data.cpython-39.pyc | Bin 1795 -> 1881 bytes server/map/input_new_data.py | 23 +++++----- server/sql_db/icedb | Bin 49152 -> 49152 bytes 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/server/data_processing/__pycache__/area_processing.cpython-39.pyc b/server/data_processing/__pycache__/area_processing.cpython-39.pyc index 36bd3436993a5dbf223824d0c9b30cbeb8de4ca5..e770151519aca70814292d4f5cc66264b00083d0 100644 GIT binary patch delta 606 zcmYjNJ8#rL5T1Sb{W#yny31h-8j1@DMKsjBBq}5x)k+{a>;<COKF2mvBCT~uNrMCu z$OSDF^r7V!P*VFJ(50tg_GAHT?R@in-*{(se|YD9m<E9hxTbF}mfOL{@bX$BlORb3 zIT0kMf)dSX#suG>A<PY0xh<T*bA>0F@FkbVlEfg*1eSgb2}mme5lCA)XCOl9N)K@) z=@VG^zjWgT?P+<?vlpxS;mh}*if?DX330nO^m@33wvGEP?z-r{(|X+y#ZJjRcy`fx z%2O<X*8i*ZZJ>gU7AiyXISxGs6)IC>3^P!c&Sub#2A`qtqmMZFn4ur(bEcq}^0eNL zkz=>GJ7<^DiE<>l1+6oPSRlz1n;p^*q`LE;N)OpaaV5igw*fU$k+M%&J5l^IRb%BS zSO1HnP4?LrLg47#(Q$W;-y~bzbH3_+^8NW$ef=E_z&Q8mF1^BR$_c04BV)b}s~I|r zsONdwys9XYieV6DnHDmw@pWZy6tDC0q^CvMkFl)GR!w%WccVPfS#J)=%B&Gqy-fF3 rRUuei)WW!b`|;BUm8DM|>7bic!N1UbHD+;yJR&xJKGCCXQ#<(sb#R1j delta 463 zcmYk2y-EW?5XWbB_L9rxa*2Axgj3K)5K@R1wtizF7GiZ^5txXmLA<1#!$K>o$i09T z_WBl9K7ft2U}0sevm)XyGqdym?amJK5I$9-Zmkx8uGaNiZ>f3{O|7kH#=v+AeN3@$ zQiH%IB__5#jDfubdK5zfTuLCdTxS0YQjaSfXe@E{W)wf(e<7BNd((+xt>>$)s|~W^ z)eJ7!<kcYxUE&5DeB_~oLJ8!xKkX%tB>K_n;<0UO18tDHlw6Eq5~S=-laM!lIVrCx z*Z9lzq<qZPX@Zc1PS3|R$FO*&>26ahjLVQ$RMG|<W^C$LCuW4%)<4yp(2o)@>HS~` z(vUInFDai8duwD|DiXN2+StS)-66vH=v;~&yNGddVbAcmc(+H%tQ$c`9gwBaESMSc zjYZv5^u2b+WG!X8^W_)Y?+%YLqgh7kQg3s0Z+|;0JE87etN8ThLO0ijR%jVN`39$4 BTNnTU diff --git a/server/data_processing/__pycache__/process_lidar_data.cpython-39.pyc b/server/data_processing/__pycache__/process_lidar_data.cpython-39.pyc index 9a3706cc4096a71462648ad5967fbd3abd35f8e1..3edb3e6aec41636e85a32723ec41ee26bef38aba 100644 GIT binary patch delta 1481 zcmb7E&u<$=6n<}J_P4!u;>331Bu>f?5eRL{fpREZT8Uc)1&X?A8wF;P*omF6-bl1s z%b=|UNUdnK7Y?XO#F<mMz=1>6e*(i9DdL3GbK%Woi`okUulBw9-hA(y=QlHZyZK!! zTrZb>px^ah|5TqX-3{kPXAwYx#2tvxcos3^dBn-H9nrHQ3pI!D*gYq5dT!(r7Sh^e zk*C;!uRIAeaEM<&i~?1XwhFrCZkRCyLXJU|0=WiN3*@PhrcM;le~KC$Oddj1Qzcc) z&<F);O4Yj485(x2TbHG67Bv)hn-rf>jTw+-8Qx}BSTq~8Xz7Z{oKv%^m9g`1wMjGc z(E`m>587&8EgZ79H06`uKco%_5Rh?%{^V)6gM<1ipm{Enl@oIECPa&}Ca1_R$!S?9 zf8zc%+{ZVW01n$CKAECqR0YmxbgeH)Y=vTkbv)&U2l1tXN3m_ltNf?R0`1Ab)jNYe zzQgbT!e<dz@<-xcaEgL)jm(X9{#Z2fAFRuGDi7_?@l1Zl9^o5#)%hM@%71oNM`faE z6;AKqH3qN=LrfqwC6U#|@r(~yf>NYRH!@)dJQ2FYLmct~5s#2ati(=v*P+*?*UNZ@ zCMGB*C?<|$4H#O9pIV~~hjtRA7I`NrNlV(2OD7XUH+9cbQ?K3|dNl7zcLs>#Q__=h zkUF}E)Yi5Msc(=eoDh4`R~mW89d1Gkrr?>Z5lFWMjY5RHW7=+OYb41Zci|sN9XXaX zoI+AZi>yM?C^cY%J+mOB!P8}q{$rj0US?p5Hk<W7%N!N|Sl7A9z5?2-@)xjy(wD(a zm$rpb(@QBHgw%H3Y)>zhRMJw08~jk;gj*@T2g{`kF?|*wcF1&ovjE0oJyz>SX9*wa z56F?w(=-7``mBAd*#P-BBCPA9I?x<_?!JVKkse;iuXyuKt#M<wuVQ^hV|`fTNiw(c zyWXl@jkQw#z<d3~qMm7Mqn8=m8=aj*#qpAkJWpo1oLBw26~92^eAQq4!0WAly0*95 z=_irb>-3A^#Ae))YkJYOE!ElFN(PZT*#F2x#?~NNkCQQ1eHn!&b+AU6ntfc}%X!D2 zfA<_MF<2o3Oth!auY?%#6?D-RA@{L_C4Lbv{>7NFkA+>wg?4;~_{-0Yt^R&*@A{ba z_hKy(@%^NiKk(00PZAo7-96PGvyH9zm3%rljj!f!2k+C>{x+DStNml}VyKg7C3;F& M75N`QoBM9-Z=Xden*aa+ delta 1117 zcmYjP&ube;6n<}J_J>!yYg<la$(Eh81jkBDut_i!TH2DH3i$y|-F9ee9M4FWY_HVS zx=<q_!UmciOw;V8P$&{P1=>??IkixF$e*B=LT~NA5OV07wF2E`-n{S4_ue-%Zyqn6 zl*64M@PNLP>|broeiv2;*APH~!UK?)&rC9!*#SQkQlMGm2iBo2?L$X8goUyiOuCv4 zr?jh}0wetPH|gnuvbERpTS3YY2st?lC&<mw^aM@iXok?hP`&?Az2WQ#WKkD%F~z)4 zLC@)8>r9Gya<O$*`TI<kG`8j`Ue=`ws6d4$>{BKya)Ihi=b4MTq8C#39(-1&m8x8# zm6_qPuIi-`GmJ0(`Uo}^3J{QTgs8Xy53w7r1LprHWYw%H-UoS3&8agqpI2v9iRRay z*6|46;sQAALwu!&iqS5xV$kOiU0bw9F~TO^V>wU6TN56|_C;3a&!(%?i>_-rf*#(+ zr+@PW#MSJj_z^E>KUl|POZGjyntg8%@LE=Oe#Q&g@6P(bp%gPXjqaah8qg=fK1)c> z94^5jOSq9AVV_SB&yh$(V)b~-rs2?VQ=VcT6BH8^6Q{9x=v&EDkDVSuQMT!RD*E=I zXVbl^Zy<pOkjsIxAAqt>Ppq7YPdKI9pyJ-wP)FsesrzovN@<H!8pxxq_HbArK5dmO zi6p*>*ZIiwDDL40Q1foqSc6Up51o(SC1(J?Y;C^(YfO(9Vsj4%<|5$sPj5I7TjagK z)nm4He%BCQea1Iyb2PfkWM~6Ma?+-WN2tzHMxy*?UMd%<VH{FM4Q1GAH=~tgyVLGA zlV&@5g3|3C-QC`a^-gSB83)D`$<(vw-Z}=43Y*zGg?Z_W?Oh!uI+ng6Z|~`5V=w7S z*P!Eeq@~l`)wjMRf=kYaks{9{kF=T*5oa55Q{_Uha!&<UvVRIoZ@x{6UGt!TiBK@t z4Kd_vyu@8}aT!a-vUr2mYp;*3=;*NX)tE({c$FgAmVbTb5;<eh?&xUD>U;6U?1{gD hwd@c74n4#5po$yWjbOz$G*i;Y8J^t>&T-dW{0}1C>=Xb1 diff --git a/server/data_processing/area_processing.py b/server/data_processing/area_processing.py index 43e289ee..81446eda 100644 --- a/server/data_processing/area_processing.py +++ b/server/data_processing/area_processing.py @@ -1,4 +1,8 @@ from math import pi, cos +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns EARTH = 6378.137 # Radius of the earth in kilometer METER = (1 / ((2 * pi / 360) * EARTH)) / 1000 # 1 meter in degree @@ -34,6 +38,7 @@ def define_gridareas(lat, lng, area_offset, grid_size): #a = dimension + subId + groupId soon to be implemented #print(a) grided_area = [] # container for an area turned into a grid of areas + #points = [] # find the main area's corner positions main_area = calculate_corners(lat, lng, area_offset) @@ -47,8 +52,6 @@ def define_gridareas(lat, lng, area_offset, grid_size): group_id = 0 i=0 - j=0 - # find the center coordinates of each area in grid to find the corner areas for y in (range(grid_size)): relative_size_lng = y / grid_size @@ -68,8 +71,20 @@ def define_gridareas(lat, lng, area_offset, grid_size): # use the center of sub areas to find the corner of each subarea corners = calculate_corners(lat_pos, lng_pos, subarea_offset) grided_area.append((sub_id, group_id + i%2, corners)) + #points.append(corners[0]) + #points.append(corners[1]) + #points.append(corners[2]) + #points.append(corners[0]) + + #lng = [-point[0] for point in points] + #lat = [point[1] for point in points] + #plt.figure() + #plt.scatter(lng, lat, marker='D') + #plt.xlabel("Longitude") + #plt.ylabel("Latitude") + #plt.show() return grided_area -#print(define_gridareas(-60,10,1500,4)) -print(define_gridareas(3435693.5,6299200.0, 20000000000,4)) \ No newline at end of file +#print(define_gridareas(-60,10,10000,4)) +#print(define_gridareas(3435693.5,6299200.0, 400000,4)) \ No newline at end of file diff --git a/server/data_processing/process_lidar_data.py b/server/data_processing/process_lidar_data.py index 6345fe11..43209e79 100644 --- a/server/data_processing/process_lidar_data.py +++ b/server/data_processing/process_lidar_data.py @@ -29,9 +29,9 @@ with laspy.open(lazData_path[0]) as fh: # check if lidar points is within range of the area selected def inArea(position, areaRange): x, y, _ = position - print(areaRange[0][0], " > ", x, " > ", areaRange[1][0], ") and (", areaRange[0][1], " < ", y, " < ", areaRange[1][1]) if (areaRange[0][0] > x > areaRange[1][0]) and (areaRange[0][1] < y < areaRange[1][1]): - print("inside area") + #print(areaRange[0][0]," > ",x," > ",areaRange[1][0],") and (",areaRange[0][1]," < ",y," < ",areaRange[1][1]) + #print("inside area", position) return True else: return False @@ -97,9 +97,10 @@ def calculate_area_data(center): #areazone = ((((-3200175 / area[0][0]) - ( - 3671212 / area[2][0]))), # ((7898422 / area[0][1]) - (area[2][1] / 4699978))) - #area = calculate_corners(center[0],center[1], 1500) + area = calculate_corners(center[0],center[1], 1500) + print("area", area) - areazone = calculate_corners(3435693.5,7550000.0, 20000000000) + #areazone = calculate_corners(3496480.06669129,7681517.961979784, 400000) #print(area[0]," area ", area[2]) #start_point = areazone[0] @@ -117,14 +118,27 @@ def calculate_area_data(center): # add two files together temporary test data(soon to be removed) ice_points = list(zip(iceOver.X,iceOver.Y,iceOver.Z)) + list(zip(iceUnder.X,iceUnder.Y,iceUnder.Z)) + max_point = max(ice_points) + min_point = min(ice_points) # area height in all the subsections #grid_area_heights = define_gridareas(-3435693.5,6299200.0, 20000000000,4) - grid_area_heights = define_gridareas(3435693.5,7550000.0, 20000000000,4) - - print("",grid_area_heights) + #grid_area_heights = define_gridareas(3496480.06669129,7681517.961979784, 400000,4) + grid_area_heights = define_gridareas(60,10, 1500,4) # find the heights of each sub-area => area-heights for sub_area in grid_area_heights: + start = min(sub_area[2]) + end = max(sub_area[2]) + + #test data + areazone = [(((min_point[0] - max_point[0]) * ((start[0]-center[0])/(area[1][0]-area[3][0]))) + (min_point[0] - max_point[0])/2 + max_point[0], + (((min_point[1] - max_point[1]) * ((start[1]-center[1])/(area[1][1]-area[3][1]))) + (min_point[1] - max_point[1])/2 + min_point[1])), + ((((min_point[0] - max_point[0]) * ((end[0]-center[0])/(area[1][0]-area[3][0]))) + (min_point[0] - max_point[0])/2 + max_point[0], + (((min_point[1] - max_point[1]) * ((end[1]-center[1])/(area[1][1]-area[3][1])))) + (min_point[1] - max_point[1])/2 + min_point[1]))] + #print("areazone", areazone) + + points_in_area = list(filter(lambda point_position: inArea(point_position, areazone), ice_points)) + area_heights.append((sub_area[0],sub_area[1],find_height(points_in_area))) #main_vector = tuple(x - y for x, y in zip(area[2], area[0])) #sub_vector = tuple(x - y for x, y in zip(sub_area[2][2], sub_area[2][0])) @@ -148,17 +162,15 @@ def calculate_area_data(center): # tuple(x + y for x, y in zip(zone_limit[1], start_point))) #print("zone_limit",zone_limit) - print(type(ice_points[0])) - ice_points = list(filter(lambda point_position: inArea(point_position, ((int(-sub_area[2][0][0]),int(sub_area[2][0][1])),(int(-sub_area[2][2][0]),int(sub_area[2][2][1])))), ice_points)) + #ice_points = list(filter(lambda point_position: inArea((float(point_position[0]),float(point_position[1]),(float(point_position[2]))), (((-sub_area[2][0][0]), (sub_area[2][0][1])), ((-sub_area[2][2][0]), (sub_area[2][2][1])))), ice_points)) #ice_points = list(filter(lambda point_position: inArea(point_position, (((-areazone[0][0]),areazone[0][1]),(-areazone[2][0],areazone[2][1]))), ice_points)) #ice_points = list(filter(lambda point_position: inArea(point_position, (((-3200000,7400000),(-3671212,7898422)))), ice_points)) - print("ice",ice_points) - area_heights.append((sub_area[0],sub_area[1],find_height(ice_points))) - return area_heights + #print("ice",ice_points) + #area_heights.append((sub_area[0],sub_area[1],find_height(ice_points))) -print(-3>-4) -print(calculate_area_data((60.0,10.0))) + return area_heights +#print(calculate_area_data((60.0,10.0))) diff --git a/server/map/__pycache__/input_new_data.cpython-39.pyc b/server/map/__pycache__/input_new_data.cpython-39.pyc index 4881eedf5ef43ca958dd3f3bc08a3990387c89ae..895a95e645fe34909fee7a3eabf48363211cab2d 100644 GIT binary patch delta 474 zcmYLDOG_J36h3DjH#c`4iCDo<3c;0yQUfjoai<7fDF}`BfrQ?OQ=Jhr6VTyKe2nfR zcj4Box^d^mh4gO}^AAe9>8hIwo{2~=obTN8edqDz{=M(FJWl|<+uwiMm+pmsHsMiK z#3Du9Lx4V}3m4x$9rdv@mDrLio*M{?FTv;2telioS(a;%qcnHS&UM0{>;KA2$V5_p z<})Ya(#!;fqnHwNkTHc<AcZP+nKdAEYLE!!<VYw+IBSiPU7#dkix0@gen&An#B-!0 zjO@9pG_qGL^z;difAn)CyZF_;@&<rFJ(YrKLNsqd;`j0J03mY`(mH8X<vz_*f2HVe z!Ujmvdu}A2aswK~tikKCo4;5>Cl#$uyA{`EE71?eFNcRk707yer+=F7@QpsUYI?;A z^mpt1i`{lKG~;eKG{Ybo+FQGOG&XI|pr_aEqwx=8L<eCv+)TEEFFR2?j`a`w0GIVD hdo{&SAjc|BqHkDe(Vv-bADHbL!eEy8$Cy56y+?DrbZ-Cv delta 405 zcmXw#KT88a5XEQi_BMNaf09BWK>~u6h!_weh=rhyAStv6f);_xNhCSZTu{SaQk=CU zY^#t|eh3Rchq1B~5lbrr?p?^hyxD#8+gI$H8@q1JaX6@Y`uMDWTSNDL(N;G{^`7eX z5dsXbn;Sl#_OE{g+lMnRGQ*58Jyw7iLpDDxLBs_Wd<!Bfa0H$y5=t+CQeJ@24EdZ@ z?@Yl{C8-*Tddl(HSVOq-1y#u`MwL2Cv%<b5GBFD3YY7st(D3XS5!7^&De%G`Ib%p2 zH*u;EY!e9G0p1-VL>5AKS<zWx#VR4?vk6~?6Y$i&X9avVOHfkIF|6h)=jt0&Rd(gN zHkD=l3t#0LIgl@;C?80}Af21eru-$<g?6Vk(VO*PV))I=S~$^LjX>6n4YcLZ=*ty) ei#zg>7V`v|mM09UUY=6J)+phBlrog>wD%9BePE*i diff --git a/server/map/input_new_data.py b/server/map/input_new_data.py index d52d93ba..600cd974 100644 --- a/server/map/input_new_data.py +++ b/server/map/input_new_data.py @@ -38,28 +38,31 @@ def input_new_Lidar_data(self, cursor, sensorId, bodyOfWater): print("area") print(areas_data) if(areas_data): + print("areas data",areas_data) for area in areas_data: - #if(len(area[2]) != 0): - average = sum(area[2])/len(area[2]) - #else: - #average = 0 - + if(len(area[2]) != 0): + average = sum(area[2])/len(area[2]) + minimum_thickness = min(area[2]) + else: + average = 0 + minimum_thickness = 0 total_measurement_average += average cursor.execute(''' INSERT INTO SubDivision(MeasurementID, SubDivisionID, GroupID, MinimumThickness, AverageThickness, CenterLatitude, CenterLongitude, Accuracy) VALUES (?,?,?,?,?,?,?,?); - ''',(measurement_id, area[0], area[1], float(min(area[2])), float(average), float(latitude), float(longitude), float(1))) + ''',(measurement_id, area[0], area[1], float(minimum_thickness), float(average), float(latitude), float(longitude), float(1))) total_measurement_average = total_measurement_average / len(areas_data) + + print("meas id ", measurement_id) # input the newly generated measurement_id and whole average thickness (soon to be implemented) cursor.execute(''' UPDATE Measurement - SET measurementID = ? AND WholeAverageThickness = ? + SET measurementID = ?, WholeAverageThickness = ? WHERE MeasurementID IS NULL AND WholeAverageThickness = 0; - ''', (int(measurement_id), total_measurement_average),) + ''', (int(measurement_id), total_measurement_average), ) else: print('No data found') - print("uwu3") # send the changes to the database cursor.connection.commit() @@ -70,4 +73,4 @@ def input_new_Lidar_data(self, cursor, sensorId, bodyOfWater): except Exception as e: print("An error occurred", e) # rollback in case of error - cursor.connection.rollback() \ No newline at end of file + cursor.connection.rollback() diff --git a/server/sql_db/icedb b/server/sql_db/icedb index 2b47b60d052434b9a32c5fa7d5bb044f59cf1974..e322b979e3371bb38f7a256e1b9416ae3b4a7659 100644 GIT binary patch literal 49152 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 delta 225 zcmWN|F-yZx7=_{QCZVm6+_x?kG@D=)N5Mbf=F%U~A%fys2Uj6$CoOk!Dj5SVHIq2# z;NVhF93*CN5CjWK1<^qVzwtchoGkOR%s*K&4C5`scqi{#_9Z}sb#53?&bL49d1{Q3 z`c(Min;BCro^Z2n;6C~baTwLYgj4p|#Ip}}WDo4NU9l3?y1b0a`e@CILki)SFW!0O znMdxp<${7S*E$>EisN1wkPC0zYm=)EDf7{8Te%W`^!h@jd`L6#8)j~!<NE2MJ4;*5 RRj*-;=1IEjCB{`($v;EdMhE}^ -- GitLab