Subversion Repositories public iLand

Rev

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

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