Rev 61 | Rev 70 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
3 | Werner | 2 | |
3 | #include "tree.h" |
||
16 | Werner | 4 | #include "core/grid.h" |
30 | Werner | 5 | #include "imagestamp.h" |
3 | Werner | 6 | |
38 | Werner | 7 | #include "core/stamp.h" |
8 | #include "treespecies.h" |
||
9 | |||
61 | Werner | 10 | |
39 | Werner | 11 | FloatGrid *Tree::m_grid = 0; |
45 | Werner | 12 | FloatGrid *Tree::m_dominanceGrid = 0; |
40 | Werner | 13 | int Tree::m_statPrint=0; |
48 | Werner | 14 | int Tree::m_statAboveZ=0; |
40 | Werner | 15 | int Tree::m_nextId=0; |
53 | Werner | 16 | float Tree::lafactor = 1.; |
58 | Werner | 17 | int Tree::m_debugid = -1; |
40 | Werner | 18 | |
3 | Werner | 19 | Tree::Tree() |
20 | { |
||
38 | Werner | 21 | m_Dbh = 0; |
22 | m_Height = 0; |
||
23 | m_species = 0; |
||
40 | Werner | 24 | m_id = m_nextId++; |
3 | Werner | 25 | } |
38 | Werner | 26 | |
15 | Werner | 27 | /** get distance and direction between two points. |
38 | Werner | 28 | returns the distance (m), and the angle between PStart and PEnd (radians) in referenced param rAngle. */ |
3 | Werner | 29 | float dist_and_direction(const QPointF &PStart, const QPointF &PEnd, float &rAngle) |
30 | { |
||
31 | float dx = PEnd.x() - PStart.x(); |
||
32 | float dy = PEnd.y() - PStart.y(); |
||
33 | float d = sqrt(dx*dx + dy*dy); |
||
34 | // direction: |
||
35 | rAngle = atan2(dx, dy); |
||
36 | return d; |
||
37 | } |
||
38 | |||
39 | |||
38 | Werner | 40 | void Tree::setup() |
41 | { |
||
42 | if (m_Dbh<=0 || m_Height<=0) |
||
43 | return; |
||
44 | // check stamp |
||
45 | Q_ASSERT_X(m_species!=0, "Tree::setup()", "species is NULL"); |
||
46 | m_stamp = m_species->stamp(m_Dbh, m_Height); |
||
47 | } |
||
39 | Werner | 48 | |
49 | void Tree::applyStamp() |
||
50 | { |
||
51 | Q_ASSERT(m_grid!=0); |
||
52 | if (!m_stamp) |
||
53 | return; |
||
54 | |||
40 | Werner | 55 | QPoint pos = m_grid->indexAt(m_Position); |
56 | int offset = m_stamp->offset(); |
||
57 | pos-=QPoint(offset, offset); |
||
58 | QPoint p; |
||
59 | |||
60 | Werner | 60 | float local_dom; // height of Z* on the current position |
40 | Werner | 61 | int x,y; |
58 | Werner | 62 | float value; |
51 | Werner | 63 | QPoint dbg(10,20); |
40 | Werner | 64 | for (x=0;x<m_stamp->size();++x) { |
65 | for (y=0;y<m_stamp->size(); ++y) { |
||
66 | p = pos + QPoint(x,y); |
||
51 | Werner | 67 | // debug pixel |
61 | Werner | 68 | //if (p==dbg) |
69 | // qDebug() << "#" << id() << "value;"<<(*m_stamp)(x,y)<<"domH"<<dom; |
||
51 | Werner | 70 | |
71 | if (m_grid->isIndexValid(p)) { |
||
72 | // mulitplicative: |
||
53 | Werner | 73 | //m_grid->valueAtIndex(p)*=(*m_stamp)(x,y); |
51 | Werner | 74 | // additiv: |
58 | Werner | 75 | // multiplicatie, v2 |
60 | Werner | 76 | local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) ); // todo: could be done more effectively (here 2 transormations are performed)... |
77 | if (local_dom<=0.f) { |
||
61 | Werner | 78 | //qDebug() << "invalid height at " << m_grid->cellCoordinates(p) << "of" << local_dom; |
79 | local_dom = 2.; |
||
60 | Werner | 80 | } |
81 | value = (*m_stamp)(x,y); |
||
82 | value = 1. - value*lafactor / local_dom; |
||
59 | Werner | 83 | value = qMax(value, 0.02f); |
58 | Werner | 84 | m_grid->valueAtIndex(p)*= value; |
51 | Werner | 85 | } |
40 | Werner | 86 | } |
87 | } |
||
58 | Werner | 88 | |
89 | m_statPrint++; // count # of stamp applications... |
||
90 | } |
||
91 | |||
62 | Werner | 92 | /** heightGrid() |
93 | This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid. |
||
94 | |||
95 | */ |
||
58 | Werner | 96 | void Tree::heightGrid() |
97 | { |
||
59 | Werner | 98 | // height of Z* |
99 | float cellsize = m_dominanceGrid->cellsize(); |
||
62 | Werner | 100 | if (m_Height < cellsize/2.) { |
59 | Werner | 101 | float &dom = m_dominanceGrid->valueAt(m_Position); // modifyable reference |
102 | dom = qMax(dom, m_Height/2.f); |
||
103 | } else { |
||
104 | QPoint p = m_dominanceGrid->indexAt(m_Position); // pos of tree on height grid |
||
62 | Werner | 105 | |
106 | int ringcount = int(floor(m_Height / cellsize)); |
||
59 | Werner | 107 | int ix, iy; |
108 | int ring; |
||
109 | QPoint pos; |
||
62 | Werner | 110 | float hdom; |
111 | float h_out = fmod(m_Height, cellsize) / 2.; |
||
59 | Werner | 112 | for (ix=-ringcount;ix<=ringcount;ix++) |
113 | for (iy=-ringcount; iy<=+ringcount; iy++) { |
||
114 | ring = qMax(abs(ix), abs(iy)); |
||
115 | QPoint pos(ix+p.x(), iy+p.y()); |
||
116 | if (m_dominanceGrid->isIndexValid(pos)) { |
||
117 | // apply value.... |
||
62 | Werner | 118 | if (ring==0) { |
119 | hdom = m_Height- cellsize/4.; |
||
120 | } else if (ring==abs(ringcount)) { |
||
121 | // outermost ring: use again height/2. |
||
122 | hdom = h_out; |
||
123 | } else { |
||
124 | hdom = m_Height- ring*cellsize; |
||
125 | } |
||
126 | m_dominanceGrid->valueAtIndex(pos) = qMax(m_dominanceGrid->valueAtIndex(pos), hdom); |
||
59 | Werner | 127 | } |
45 | Werner | 128 | |
59 | Werner | 129 | } |
130 | } |
||
131 | |||
39 | Werner | 132 | } |
40 | Werner | 133 | |
134 | double Tree::readStamp() |
||
135 | { |
||
51 | Werner | 136 | if (!m_stamp) |
137 | return 0.; |
||
47 | Werner | 138 | const Stamp *stamp = m_stamp->reader(); |
40 | Werner | 139 | if (!stamp) |
140 | return 0.; |
||
141 | QPoint pos = m_grid->indexAt(m_Position); |
||
142 | int offset = stamp->offset(); |
||
143 | pos-=QPoint(offset, offset); |
||
144 | QPoint p; |
||
145 | |||
146 | int x,y; |
||
147 | double sum=0.; |
||
148 | for (x=0;x<stamp->size();++x) { |
||
149 | for (y=0;y<stamp->size(); ++y) { |
||
150 | p = pos + QPoint(x,y); |
||
151 | if (m_grid->isIndexValid(p)) |
||
152 | sum += m_grid->valueAtIndex(p) * (*stamp)(x,y); |
||
153 | } |
||
154 | } |
||
53 | Werner | 155 | float eigenvalue = m_stamp->readSum() * lafactor; |
156 | mImpact = sum - eigenvalue;// additive |
||
157 | float dom_height = (*m_dominanceGrid)[m_Position]; |
||
158 | if (dom_height>0.) |
||
159 | mImpact = mImpact / dom_height; |
||
160 | |||
161 | //mImpact = sum + eigenvalue;// multiplicative |
||
48 | Werner | 162 | // read dominance field... |
53 | Werner | 163 | |
48 | Werner | 164 | if (dom_height < m_Height) { |
165 | // if tree is higher than Z*, the dominance height |
||
166 | // a part of the crown is in "full light". |
||
167 | // total value = zstar/treeheight*value + 1-zstar/height |
||
168 | // reformulated to: |
||
169 | mImpact = mImpact * dom_height/m_Height ; |
||
170 | m_statAboveZ++; |
||
171 | } |
||
47 | Werner | 172 | if (fabs(mImpact < 0.000001)) |
173 | mImpact = 0.f; |
||
174 | qDebug() << "Tree #"<< id() << "value" << sum << "eigenvalue" << eigenvalue << "Impact" << mImpact; |
||
42 | Werner | 175 | return mImpact; |
40 | Werner | 176 | } |
177 | |||
58 | Werner | 178 | double Tree::readStampMul() |
179 | { |
||
180 | if (!m_stamp) |
||
181 | return 0.; |
||
182 | const Stamp *reader = m_stamp->reader(); |
||
183 | if (!reader) |
||
184 | return 0.; |
||
185 | QPoint pos_reader = m_grid->indexAt(m_Position); |
||
186 | |||
187 | int offset_reader = reader->offset(); |
||
188 | int offset_writer = m_stamp->offset(); |
||
189 | int d_offset = offset_writer - offset_reader; |
||
190 | |||
191 | QPoint pos_writer=pos_reader - QPoint(offset_writer, offset_writer); |
||
192 | pos_reader-=QPoint(offset_reader, offset_reader); |
||
193 | QPoint p; |
||
194 | |||
195 | float dom_height = (*m_dominanceGrid)[m_Position]; |
||
196 | |||
197 | int x,y; |
||
198 | double sum=0.; |
||
199 | double value, own_value; |
||
200 | for (x=0;x<reader->size();++x) { |
||
201 | for (y=0;y<reader->size(); ++y) { |
||
202 | p = pos_reader + QPoint(x,y); |
||
203 | if (m_grid->isIndexValid(p)) { |
||
204 | own_value = 1. - m_stamp->offsetValue(x,y,d_offset)*lafactor /dom_height; |
||
59 | Werner | 205 | own_value = qMax(own_value, 0.02); |
58 | Werner | 206 | value = m_grid->valueAtIndex(p) / own_value; // remove impact of focal tree |
207 | if (value>0.) |
||
208 | sum += value * (*reader)(x,y); |
||
209 | //value = (1. - m_stamp->offsetValue(x,y,d_offset)/dom_height); |
||
210 | //value = (1 - m_grid->valueAtIndex(p)) / (m_stamp->offsetValue(x,y,d_offset)/dom_height * lafactor); |
||
211 | if (isDebugging()) { |
||
212 | qDebug() << "Tree" << id() << "Coord" << p << "gridvalue" << m_grid->valueAtIndex(p) << "value" << value << "reader" << (*reader)(x,y) << "writer" << m_stamp->offsetValue(x,y,d_offset); |
||
213 | } |
||
214 | |||
215 | } |
||
216 | } |
||
217 | } |
||
218 | mImpact = sum; |
||
219 | // read dominance field... |
||
220 | if (dom_height < m_Height) { |
||
221 | // if tree is higher than Z*, the dominance height |
||
222 | // a part of the crown is in "full light". |
||
223 | m_statAboveZ++; |
||
224 | mImpact = 1. - (1. - mImpact)*dom_height/m_Height; |
||
225 | } |
||
226 | if (mImpact > 1) |
||
227 | mImpact = 1.f; |
||
61 | Werner | 228 | //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact; |
58 | Werner | 229 | return mImpact; |
230 | } |
||
231 | |||
232 | |||
40 | Werner | 233 | void Tree::resetStatistics() |
234 | { |
||
235 | m_statPrint=0; |
||
48 | Werner | 236 | m_statAboveZ=0; |
40 | Werner | 237 | m_nextId=1; |
238 | } |