Subversion Repositories public iLand

Rev

Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1
 
17 Werner 2
#include "lightroom.h"
83 Werner 3
#include "../tools/helper.h"
17 Werner 4
 
62 Werner 5
#include <QtCore>
6
 
17 Werner 7
LightRoom::LightRoom()
8
{
24 Werner 9
    m_roomObject = 0;
404 werner 10
    m_aggregationMode = 0;
17 Werner 11
}
12
 
18 Werner 13
/** setup routine.
14
  sets up datastructures (3d space, hemi grids)
32 Werner 15
  @param dimx size of space in x direction [m]
16
  @param dimy size of space in y direction [m]
17
  @param dimz size of space in z direction [m]
18 Werner 18
  @param cellsize metric length of cells (used for all 3 dimensions).
19
  @param hemigridsize size of hemigrid-cells (in degrees).
20
  @param latitude lat. in degrees.
21
  @param diffus_frac fraction [0..1] of diffus radiation of global radiation. */
32 Werner 22
void LightRoom::setup(const double dimx, const double dimy, const double dimz,
18 Werner 23
                      const double cellsize, const double hemigridsize,
24
                      const double latitude, const double diffus_frac)
17 Werner 25
{
19 Werner 26
    DebugTimer t1("setup of lightroom");
32 Werner 27
    m_countX = int(dimx / cellsize);
28
    m_countY = int(dimy / cellsize);
29
    m_countZ = int(dimz / cellsize);
30
 
17 Werner 31
    m_cellsize = cellsize;
22 Werner 32
    if (m_countX%2==0) m_countX++; // make uneven
33
    if (m_countY%2==0) m_countY++;
32 Werner 34
 
35
    QRectF rect(-m_countX/2.*cellsize, -m_countY/2.*cellsize, m_countX*cellsize, m_countY*cellsize);
36
    qDebug() << "Lightroom size: " << m_countX << "/" << m_countY << "/" << m_countZ << " elements. rect: " << rect;
37
 
38
 
22 Werner 39
    m_2dvalues.setup(rect, cellsize);
18 Werner 40
    m_2dvalues.initialize(0.);
27 Werner 41
 
18 Werner 42
    // setup hemigrids
43
    SolarRadiation solar;
44
    solar.setLatidude(latitude); // austria
45
    solar.setVegetationPeriod(0,367); // no. veg. period
46
    solar.setDiffusRadFraction(diffus_frac); // 50% diffuse radiation
47
    // calculate global radiation values
19 Werner 48
    DebugTimer t2("calculate solar radiation matrix");
18 Werner 49
    solar.calculateRadMatrix(hemigridsize, m_solarGrid);
19 Werner 50
    t2.showElapsed();
18 Werner 51
    m_shadowGrid.setup(hemigridsize); // setup size
29 Werner 52
    m_solarrad_factor = 1. / m_solarGrid.sum(RAD(45)); // sum of rad. > 45°
44 Werner 53
    m_centervalue = 0.;
17 Werner 54
}
22 Werner 55
 
27 Werner 56
double LightRoom::calculateGridAtPoint(const double p_x, const double p_y, const double p_z, bool fillShadowGrid)
24 Werner 57
{
25 Werner 58
    if (!m_roomObject)
59
        return 0;
60
    // check feasibility
61
    if (m_roomObject->noHitGuaranteed(p_x, p_y, p_z)) {
27 Werner 62
        //qDebug()<<"skipped";
25 Werner 63
        return 0;
64
    }
65
 
27 Werner 66
    if (fillShadowGrid)
67
        m_shadowGrid.clear(0.);
25 Werner 68
 
24 Werner 69
    // start with 45°
25 Werner 70
    int ie = m_shadowGrid.indexElevation(RAD(45));
71
    int ia = 0;
72
    int max_a = m_shadowGrid.matrixCountAzimuth();
73
    int max_e = m_shadowGrid.matrixCountElevation();
74
    double elevation, azimuth;
27 Werner 75
    double solar_sum=0;
31 Werner 76
    int hit;
25 Werner 77
    int c_hit = 0;
78
    int c_test = 0;
79
    for (;ie<max_e;ie++){
80
        for (ia=0;ia<max_a;ia++) {
402 werner 81
            azimuth = m_shadowGrid.azimuthNorth(ia);
25 Werner 82
            elevation = m_shadowGrid.elevation(ie);
83
            hit = m_roomObject->hittest(p_x, p_y, p_z,azimuth,elevation);
31 Werner 84
            // if inside the crown: do nothing and return.
38 Werner 85
            // 20090708: if inside the crown: return "totally dark"
31 Werner 86
            if (hit==-1)
38 Werner 87
                return 1;
25 Werner 88
            //qDebug() << "testing azimuth" << GRAD(azimuth) << "elevation" << GRAD(elevation)<<"hit"<<hit;
89
            c_test++;
31 Werner 90
            if (hit==1) {
27 Werner 91
                // retrieve value from solar grid
92
                // Sum(cells) of solargrid =1 -> the sum of all "shadowed" pixels therefore is already the "ratio" of shaded/total radiation
93
                solar_sum += m_solarGrid.rGetByIndex(ia, ie);
94
                if (fillShadowGrid)
95
                  m_shadowGrid.rGetByIndex(ia, ie) = m_solarGrid.rGetByIndex(ia, ie);
25 Werner 96
                c_hit++;
97
            }
98
        }
99
    }
29 Werner 100
    // solar-rad-factor = 1/(sum rad > 45°)
101
    return solar_sum * m_solarrad_factor;
27 Werner 102
    //double ratio = c_hit / double(c_test);
103
    //qDebug() << "tested"<< c_test<<"hit count:" << c_hit<<"ratio"<<c_hit/double(c_test)<<"total sum"<<m_shadowGrid.getSum();
104
    //return ratio; // TODO: the global radiation is not calculated!!!!!
105
 
24 Werner 106
}
22 Werner 107
 
25 Werner 108
void LightRoom::calculateFullGrid()
109
{
27 Werner 110
    float *v = m_2dvalues.begin();
111
    float *vend = m_2dvalues.end();
25 Werner 112
    int z;
113
    QPoint pindex;
114
    QPointF coord;
115
    double hit_ratio;
116
    DebugTimer t("calculate full grid");
117
    int c=0;
27 Werner 118
    float maxh = m_roomObject->maxHeight();
28 Werner 119
 
27 Werner 120
    float *values = new float[m_countZ];
68 Werner 121
 
400 werner 122
    //double h_realized;
27 Werner 123
    double sum;
68 Werner 124
 
25 Werner 125
    while (v!=vend) {
27 Werner 126
        pindex = m_2dvalues.indexOf(v);
105 Werner 127
        coord = m_2dvalues.cellCenterPoint(pindex);
62 Werner 128
        double hor_distance = sqrt(coord.x()*coord.x() + coord.y()*coord.y());
129
 
27 Werner 130
        for (z=0;z<m_countZ && z*m_cellsize <= maxh;z++) {
28 Werner 131
            // only calculate values up to the 45° line
132
            // tan(45°)=1 -> so this is the case when the distance p->tree > height of the tree
62 Werner 133
            if (hor_distance > maxh-z*m_cellsize)
28 Werner 134
                break;
27 Werner 135
            hit_ratio = calculateGridAtPoint(coord.x(), coord.y(), // coords x,y
136
                                             z*m_cellsize,false); // heigth (z), false: do not clear and fill shadow grid structure
31 Werner 137
            // stop calculating when return = -1 (e.g. when inside the crown)
138
            if (hit_ratio==-1)
139
                break;
44 Werner 140
            values[z]=hit_ratio; // this value * height of cells
25 Werner 141
        }
27 Werner 142
        // calculate average
143
        sum = 0;
38 Werner 144
        // 20090708: do not average, but keep the sum
28 Werner 145
        // aggregate mean for all cells with angles>45° to the tree-top!
43 Werner 146
        // 20090711: again average it, but now include the tree top.
52 Werner 147
        // 20090713: go back to sum...
68 Werner 148
        // 20090812: remove the weighting for the 10m cells again.
400 werner 149
        // 20100520: again, use the average value (per meter)
27 Werner 150
        for(int i=0;i<z;i++)
151
            sum+=values[i];
404 werner 152
        if (m_aggregationMode==0) {
153
            // average shadow / meter height (within the 45° cone)
154
            if (z)
155
                sum/=float(z);
156
        } else {
157
            // aggregation: sum
158
            sum *= m_cellsize;  // multiply with height (shadow * meter)
159
        }
160
        *v = sum; // store in matrix
400 werner 161
        // sum
62 Werner 162
 
25 Werner 163
        v++;
164
        c++;
165
        if (c%1000==0) {
72 Werner 166
            qDebug() << c << "processed...time: ms: " << t.elapsed();
25 Werner 167
            QCoreApplication::processEvents();
168
        }
43 Werner 169
        // save value of the middle column...
170
        m_centervalue = m_2dvalues(0.f, 0.f);
62 Werner 171
    } // while(v!=vend)
25 Werner 172
}
173
 
22 Werner 174
//////////////////////////////////////////////////////////
175
// Lightroom Object
176
//////////////////////////////////////////////////////////
177
 
23 Werner 178
LightRoomObject::~LightRoomObject()
179
{
180
    if (m_radiusFormula)
181
        delete m_radiusFormula;
182
}
183
 
22 Werner 184
void LightRoomObject::setuptree(const double height, const double crownheight, const QString &formula)
185
{
23 Werner 186
    if (m_radiusFormula)
187
        delete m_radiusFormula;
22 Werner 188
 
23 Werner 189
    m_radiusFormula = new Expression(formula);
30 Werner 190
 
23 Werner 191
    m_baseradius = m_radiusFormula->calculate(crownheight/height);
192
    m_height = height;
193
    m_crownheight = crownheight;
27 Werner 194
    double h=0., r;
195
    m_treeHeights.clear();
196
    // preprocess crown radii for each meter step
197
    while (h<=m_height) {
198
        if (h<m_crownheight)
199
            r=0.;
200
        else
201
            r = m_radiusFormula->calculate(h/m_height);
202
        m_treeHeights.push_back(r);
203
        h++;
204
    }
22 Werner 205
}
32 Werner 206
 
207
 
208
// Angle-function. return the  difference between
209
// two angles as a radian value between -pi..pi [-180..180°]
210
// >0: directionB is "left" (ccw) of directionA, <0: "right", clockwise
211
// e.g.: result=-10: 10° cw, result=10°: 10° ccw
212
// result:-180/+180: antiparallel.
213
double DiffOfAngles(double DirectionA, double DirectionB)
214
{
215
    DirectionA = fmod(DirectionA, PI2); // -> -2pi .. 2pi
216
    if (DirectionA < 0) DirectionA+=PI2; // -> 0..2pi (e.g.: AngleA = -30 -> 330)
217
    DirectionB = fmod(DirectionB, PI2);
218
    if (DirectionB < 0) DirectionB+=PI2;
219
    double Delta = DirectionB - DirectionA;
220
    if (Delta<0) {
221
      if (Delta<-M_PI)
222
         return Delta  + PI2; // ccw
223
      else
224
         return Delta; // cw
225
    } else {
226
      if (Delta>M_PI)
227
         return Delta - PI2; // cw
228
      else
229
         return Delta;  // ccw
230
    }
231
 
232
}
233
 
234
 
235
// Angle-function. return the absolute difference between
236
// two angles as a radian value between 0..pi [0..180°]
237
// e.g. 10° = +10° or -10°; maximum value is 180°
238
double AbsDiffOfAngles(double AngleA, double AngleB)
239
{
240
     return fabs(DiffOfAngles(AngleA, AngleB));
241
}
242
 
243
 
23 Werner 244
/** The tree is located in x/y=0/0.
245
*/
31 Werner 246
int LightRoomObject::hittest(const double p_x, const double p_y, const double p_z,
22 Werner 247
                              const double azimuth_rad, const double elevation_rad)
248
{
27 Werner 249
    bool inside_crown=false;
23 Werner 250
    if (p_z > m_height)
31 Werner 251
        return 0;
25 Werner 252
    // Test 1: does the ray (azimuth) direction hit the crown?
23 Werner 253
    double phi = atan2(-p_y, -p_x); // angle between P and the tree center
254
    double dist2d = sqrt(p_x*p_x + p_y*p_y); // distance at ground
28 Werner 255
    //if (dist2d==0)
256
    //    return true;
22 Werner 257
 
32 Werner 258
    double alpha = DiffOfAngles(phi, azimuth_rad); //  phi - azimuth_rad; // angle between the ray and the center of the tree
25 Werner 259
    if (dist2d>m_baseradius) { // test only, if p not the crown
23 Werner 260
        double half_max_angle = asin(m_baseradius / dist2d); // maximum deviation angle from direct connection p - tree where ray hits maxradius
261
        if (fabs(alpha) > half_max_angle)
31 Werner 262
            return 0;
23 Werner 263
    } else {
27 Werner 264
        inside_crown = true;
23 Werner 265
        // test if p is inside the crown
266
        if (p_z<=m_height && p_z>=m_crownheight) {
267
            double radius_hit = m_radiusFormula->calculate(p_z / m_height);
31 Werner 268
            if (dist2d <= radius_hit)
269
                return -1;
23 Werner 270
        }
271
    }
272
 
273
    // Test 2: test if the crown-"plate" at the bottom of the crown is hit.
25 Werner 274
    if (elevation_rad>0. && p_z<m_crownheight) {
23 Werner 275
        // calc. base distance between p and the point where the height of the ray reaches the bottom of the crown:
276
        double r_hitbottom = dist2d; // for 90°
277
        if (elevation_rad < M_PI_2) {
278
            double d_hitbottom = (m_crownheight - p_z) / tan(elevation_rad);
279
            // calc. position (projected) of that hit point
27 Werner 280
            double rx = p_x + cos(azimuth_rad)*d_hitbottom;
281
            double ry = p_y + sin(azimuth_rad)*d_hitbottom;
23 Werner 282
            r_hitbottom = sqrt(rx*rx + ry*ry);
283
        }
284
        if (r_hitbottom <= m_baseradius)
31 Werner 285
            return 1;
23 Werner 286
    }
27 Werner 287
    // Test 3: test for height steps...
288
    // distance from p to the plane normal to p-vector through the center of the tree
31 Werner 289
    // do only when p is
27 Werner 290
    double rx,ry,rhit;
291
    double d_center = cos(alpha)*dist2d;
31 Werner 292
    if (d_center>=0) {
293
        double h_center = p_z + d_center*tan(elevation_rad);
294
        if (h_center<=m_height && h_center>=m_crownheight) {
295
            rx = p_x + cos(azimuth_rad)*d_center;
296
            ry = p_y + sin(azimuth_rad)*d_center;
297
            rhit = sqrt(rx*rx + ry*ry);
298
            double r_h = m_radiusFormula->calculate(h_center / m_height);
299
            if (rhit < r_h)
300
                return 1;
301
        }
27 Werner 302
    }
303
 
304
    // Test 4: "walk" through crown using 1m steps.
305
    double h=floor(p_z);
306
    double d_1m = 1 / tan(elevation_rad); //projected ground distance equivalent of 1m height difference
307
    double d_cur = 0;
308
    if (h!=p_z) {
309
       d_cur += ((h+1)-p_z)*d_1m;
310
    }
311
 
312
    while (h<=m_height) {
313
        double r_tree = m_treeHeights[int(h)];
314
        rx = p_x + cos(azimuth_rad)*d_cur;
315
        ry = p_y + sin(azimuth_rad)*d_cur;
316
        rhit = rx*rx + ry*ry;
31 Werner 317
        // hit if inside of radius
28 Werner 318
        if (rhit < r_tree*r_tree)
31 Werner 319
            return 1;
320
        // no hit: if formerly was inside crown and now left
27 Werner 321
        if (inside_crown && rhit > m_baseradius*m_baseradius)
31 Werner 322
            return 0;
27 Werner 323
 
31 Werner 324
        // enter crown
27 Werner 325
        if (!inside_crown && rhit <=  m_baseradius*m_baseradius)
326
            inside_crown = true;
327
        d_cur+=d_1m;
328
        h++;
329
    }
31 Werner 330
    return 0;
23 Werner 331
 
332
 
22 Werner 333
}
25 Werner 334
 
335
bool LightRoomObject::noHitGuaranteed(const double p_x, const double p_y, const double p_z)
336
{
337
    // 1. simple: compare height...
338
    if (p_z > m_height)
339
        return true;
340
    // 2. 45° test:
341
    if (p_z > m_height - sqrt(p_x*p_x + p_y*p_y)) // 45°: height = distance from tree center
342
        return true;
343
 
344
    return false;
345
}
346