edgedensity.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /****************************************************************************
  2. *
  3. * MODULE: r.li.edgedensity
  4. * AUTHOR(S): Serena Pallecchi student of Computer Science University of Pisa (Italy)
  5. * Commission from Faunalia Pontedera (PI) www.faunalia.it
  6. * Rewrite: Markus Metz
  7. *
  8. * PURPOSE: calculates edge density index
  9. * COPYRIGHT: (C) 2006-2014 by the GRASS Development Team
  10. *
  11. * This program is free software under the GNU General Public
  12. * License (>=v2). Read the file COPYING that comes with GRASS
  13. * for details.
  14. *
  15. *****************************************************************************/
  16. #include <stdlib.h>
  17. #include <fcntl.h> /* for O_RDONLY usage */
  18. #include <math.h>
  19. #include <grass/gis.h>
  20. #include <grass/raster.h>
  21. #include <grass/glocale.h>
  22. #include "../r.li.daemon/avlDefs.h"
  23. #include "../r.li.daemon/avl.h"
  24. #include "../r.li.daemon/daemon.h"
  25. rli_func edgedensity;
  26. int calculate(int fd, struct area_entry *ad, char **par, double *result);
  27. int calculateD(int fd, struct area_entry *ad, char **par, double *result);
  28. int calculateF(int fd, struct area_entry *ad, char **par, double *result);
  29. static int brdr = 1;
  30. int main(int argc, char *argv[])
  31. {
  32. struct Option *raster, *conf, *output, *class;
  33. struct Flag *flag_brdr;
  34. struct GModule *module;
  35. char **par = NULL;
  36. G_gisinit(argv[0]);
  37. module = G_define_module();
  38. module->description =
  39. _("Calculates edge density index on a raster map, using a 4 neighbour algorithm");
  40. G_add_keyword(_("raster"));
  41. G_add_keyword(_("landscape structure analysis"));
  42. G_add_keyword(_("patch index"));
  43. /* define options */
  44. raster = G_define_standard_option(G_OPT_R_INPUT);
  45. conf = G_define_standard_option(G_OPT_F_INPUT);
  46. conf->key = "config";
  47. conf->description = _("Configuration file");
  48. conf->required = YES;
  49. output = G_define_standard_option(G_OPT_R_OUTPUT);
  50. class = G_define_option();
  51. class->key = "patch_type";
  52. class->type = TYPE_STRING;
  53. class->required = NO;
  54. class->multiple = NO;
  55. class->label = _("The value of the patch type");
  56. class->description = _("It can be integer, double or float; "
  57. "it will be changed in function of map type");
  58. flag_brdr = G_define_flag();
  59. flag_brdr->key = 'b';
  60. flag_brdr->description = _("Exclude border edges");
  61. if (G_parser(argc, argv))
  62. exit(EXIT_FAILURE);
  63. if (class->answer == NULL)
  64. par = NULL;
  65. else
  66. par = &class->answer;
  67. brdr = flag_brdr->answer == 0;
  68. return calculateIndex(conf->answer, edgedensity, par, raster->answer,
  69. output->answer);
  70. }
  71. int edgedensity(int fd, char **par, struct area_entry *ad, double *result)
  72. {
  73. int ris = -1;
  74. double indice = 0;
  75. switch (ad->data_type) {
  76. case CELL_TYPE:
  77. {
  78. ris = calculate(fd, ad, par, &indice);
  79. break;
  80. }
  81. case DCELL_TYPE:
  82. {
  83. ris = calculateD(fd, ad, par, &indice);
  84. break;
  85. }
  86. case FCELL_TYPE:
  87. {
  88. ris = calculateF(fd, ad, par, &indice);
  89. break;
  90. }
  91. default:
  92. {
  93. G_fatal_error("data type unknown");
  94. return RLI_ERRORE;
  95. }
  96. }
  97. if (ris != RLI_OK) {
  98. return RLI_ERRORE;
  99. }
  100. *result = indice;
  101. return RLI_OK;
  102. }
  103. int calculate(int fd, struct area_entry *ad, char **par, double *result)
  104. {
  105. CELL *buf, *buf_sup, *buf_null;
  106. CELL corrCell, precCell, supCell;
  107. CELL ptype;
  108. long nedges, area;
  109. int i, j;
  110. int mask_fd, *mask_buf, *mask_sup, *mask_tmp, masked;
  111. struct Cell_head hd;
  112. Rast_get_window(&hd);
  113. /* open mask if needed */
  114. mask_fd = -1;
  115. mask_buf = mask_sup = NULL;
  116. masked = FALSE;
  117. if (ad->mask == 1) {
  118. if ((mask_fd = open(ad->mask_name, O_RDONLY, 0755)) < 0)
  119. return RLI_ERRORE;
  120. mask_buf = G_malloc(ad->cl * sizeof(int));
  121. if (mask_buf == NULL) {
  122. G_fatal_error("malloc mask_buf failed");
  123. return RLI_ERRORE;
  124. }
  125. mask_sup = G_malloc(ad->cl * sizeof(int));
  126. if (mask_sup == NULL) {
  127. G_fatal_error("malloc mask_buf failed");
  128. return RLI_ERRORE;
  129. }
  130. for (j = 0; j < ad->cl; j++)
  131. mask_buf[j] = 0;
  132. masked = TRUE;
  133. }
  134. buf_null = Rast_allocate_c_buf();
  135. if (buf_null == NULL) {
  136. G_fatal_error("malloc buf_null failed");
  137. return RLI_ERRORE;
  138. }
  139. /* the first time buf_sup is all null */
  140. Rast_set_c_null_value(buf_null, Rast_window_cols());
  141. buf_sup = buf_null;
  142. if (par != NULL) { /* only 1 class */
  143. char *sval;
  144. sval = par[0];
  145. ptype = atoi(sval);
  146. }
  147. else
  148. Rast_set_c_null_value(&ptype, 1);
  149. nedges = 0;
  150. area = 0;
  151. /* for each raster row */
  152. for (i = 0; i < ad->rl; i++) {
  153. /* read row of raster */
  154. buf = RLI_get_cell_raster_row(fd, i + ad->y, ad);
  155. if (i > 0) /* not first row */
  156. buf_sup = RLI_get_cell_raster_row(fd, i - 1 + ad->y, ad);
  157. /* read mask if needed */
  158. if (masked) {
  159. mask_tmp = mask_sup;
  160. mask_sup = mask_buf;
  161. mask_buf = mask_tmp;
  162. if (read(mask_fd, mask_buf, (ad->cl * sizeof(int))) < 0)
  163. return RLI_ERRORE;
  164. }
  165. Rast_set_c_null_value(&precCell, 1);
  166. for (j = 0; j < ad->cl; j++) {
  167. corrCell = buf[j + ad->x];
  168. if (masked && mask_buf[j] == 0) {
  169. Rast_set_c_null_value(&corrCell, 1);
  170. }
  171. supCell = buf_sup[j + ad->x];
  172. if (masked && (mask_sup[j] == 0)) {
  173. Rast_set_c_null_value(&supCell, 1);
  174. }
  175. if (brdr) {
  176. if (!Rast_is_c_null_value(&corrCell)) {
  177. area++;
  178. if (Rast_is_c_null_value(&ptype) || corrCell == ptype) {
  179. if (Rast_is_c_null_value(&precCell) || precCell != corrCell) {
  180. nedges++;
  181. }
  182. if (Rast_is_c_null_value(&supCell) || supCell != corrCell) {
  183. nedges++;
  184. }
  185. /* right and bottom */
  186. if (i == ad->rl - 1)
  187. nedges++;
  188. if (j == ad->cl - 1)
  189. nedges++;
  190. }
  191. }
  192. else /* corrCell == NULL */ {
  193. if (!Rast_is_c_null_value(&precCell)) {
  194. if (Rast_is_c_null_value(&ptype) || precCell == ptype) {
  195. nedges++;
  196. }
  197. }
  198. if (!Rast_is_c_null_value(&supCell)) {
  199. if (Rast_is_c_null_value(&ptype) || supCell == ptype) {
  200. nedges++;
  201. }
  202. }
  203. }
  204. }
  205. else {
  206. /* exclude border edges */
  207. if (!Rast_is_c_null_value(&corrCell)) {
  208. area++;
  209. if (Rast_is_c_null_value(&ptype) || corrCell == ptype) {
  210. if (j > 0 && !(masked && mask_buf[j - 1] == 0) &&
  211. (Rast_is_c_null_value(&precCell) || precCell != corrCell)) {
  212. nedges++;
  213. }
  214. if (i > 0 && !(masked && mask_sup[i - 1] == 0) &&
  215. (Rast_is_c_null_value(&supCell) || supCell != corrCell)) {
  216. nedges++;
  217. }
  218. }
  219. }
  220. else if (Rast_is_c_null_value(&corrCell) && !(masked && mask_buf[j] == 0)) {
  221. if (!Rast_is_c_null_value(&precCell)) {
  222. if (Rast_is_c_null_value(&ptype) || precCell == ptype) {
  223. nedges++;
  224. }
  225. }
  226. if (!Rast_is_c_null_value(&supCell)) {
  227. if (Rast_is_c_null_value(&ptype) || supCell == ptype) {
  228. nedges++;
  229. }
  230. }
  231. }
  232. }
  233. precCell = corrCell;
  234. }
  235. }
  236. /* calculate index */
  237. if (area > 0) {
  238. double EW_DIST1, EW_DIST2, NS_DIST1, NS_DIST2;
  239. double elength, cell_size;
  240. /* calculate distance */
  241. G_begin_distance_calculations();
  242. /* EW Dist at North edge */
  243. EW_DIST1 = G_distance(hd.east, hd.north, hd.west, hd.north);
  244. /* EW Dist at South Edge */
  245. EW_DIST2 = G_distance(hd.east, hd.south, hd.west, hd.south);
  246. /* NS Dist at East edge */
  247. NS_DIST1 = G_distance(hd.east, hd.north, hd.east, hd.south);
  248. /* NS Dist at West edge */
  249. NS_DIST2 = G_distance(hd.west, hd.north, hd.west, hd.south);
  250. elength = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols) +
  251. (NS_DIST1 + NS_DIST2) / (2 * hd.rows)) / 2;
  252. cell_size = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols)) *
  253. ((NS_DIST1 + NS_DIST2) / (2 * hd.rows));
  254. *result = (double) nedges * elength * 10000 / (area * cell_size);
  255. }
  256. else
  257. Rast_set_d_null_value(result, 1);
  258. if (masked) {
  259. close(mask_fd);
  260. G_free(mask_buf);
  261. G_free(mask_sup);
  262. }
  263. G_free(buf_null);
  264. return RLI_OK;
  265. }
  266. int calculateD(int fd, struct area_entry *ad, char **par, double *result)
  267. {
  268. DCELL *buf, *buf_sup, *buf_null;
  269. DCELL corrCell, precCell, supCell;
  270. DCELL ptype;
  271. long nedges, area;
  272. int i, j;
  273. int mask_fd, *mask_buf, *mask_sup, *mask_tmp, masked;
  274. struct Cell_head hd;
  275. Rast_get_window(&hd);
  276. /* open mask if needed */
  277. mask_fd = -1;
  278. mask_buf = mask_sup = NULL;
  279. masked = FALSE;
  280. if (ad->mask == 1) {
  281. if ((mask_fd = open(ad->mask_name, O_RDONLY, 0755)) < 0)
  282. return RLI_ERRORE;
  283. mask_buf = G_malloc(ad->cl * sizeof(int));
  284. if (mask_buf == NULL) {
  285. G_fatal_error("malloc mask_buf failed");
  286. return RLI_ERRORE;
  287. }
  288. mask_sup = G_malloc(ad->cl * sizeof(int));
  289. if (mask_sup == NULL) {
  290. G_fatal_error("malloc mask_buf failed");
  291. return RLI_ERRORE;
  292. }
  293. for (j = 0; j < ad->cl; j++)
  294. mask_buf[j] = 0;
  295. masked = TRUE;
  296. }
  297. buf_null = Rast_allocate_d_buf();
  298. if (buf_null == NULL) {
  299. G_fatal_error("malloc buf_null failed");
  300. return RLI_ERRORE;
  301. }
  302. /* the first time buf_sup is all null */
  303. Rast_set_d_null_value(buf_null, Rast_window_cols());
  304. buf_sup = buf_null;
  305. if (par != NULL) { /* only 1 class */
  306. char *sval;
  307. sval = par[0];
  308. ptype = atof(sval);
  309. }
  310. else
  311. Rast_set_d_null_value(&ptype, 1);
  312. nedges = 0;
  313. area = 0;
  314. /* for each raster row */
  315. for (i = 0; i < ad->rl; i++) {
  316. /* read row of raster */
  317. buf = RLI_get_dcell_raster_row(fd, i + ad->y, ad);
  318. if (i > 0) /* not first row */
  319. buf_sup = RLI_get_dcell_raster_row(fd, i - 1 + ad->y, ad);
  320. /* read mask if needed */
  321. if (masked) {
  322. mask_tmp = mask_sup;
  323. mask_sup = mask_buf;
  324. mask_buf = mask_tmp;
  325. if (read(mask_fd, mask_buf, (ad->cl * sizeof(int))) < 0)
  326. return RLI_ERRORE;
  327. }
  328. Rast_set_d_null_value(&precCell, 1);
  329. for (j = 0; j < ad->cl; j++) {
  330. corrCell = buf[j + ad->x];
  331. if (masked && mask_buf[j] == 0) {
  332. Rast_set_d_null_value(&corrCell, 1);
  333. }
  334. supCell = buf_sup[j + ad->x];
  335. if (masked && (mask_sup[j] == 0)) {
  336. Rast_set_d_null_value(&supCell, 1);
  337. }
  338. if (brdr) {
  339. if (!Rast_is_d_null_value(&corrCell)) {
  340. area++;
  341. if (Rast_is_d_null_value(&ptype) || corrCell == ptype) {
  342. if (Rast_is_d_null_value(&precCell) || precCell != corrCell) {
  343. nedges++;
  344. }
  345. if (Rast_is_d_null_value(&supCell) || supCell != corrCell) {
  346. nedges++;
  347. }
  348. /* right and bottom */
  349. if (i == ad->rl - 1)
  350. nedges++;
  351. if (j == ad->cl - 1)
  352. nedges++;
  353. }
  354. }
  355. else /* corrCell == NULL */ {
  356. if (!Rast_is_d_null_value(&precCell)) {
  357. if (Rast_is_d_null_value(&ptype) || precCell == ptype) {
  358. nedges++;
  359. }
  360. }
  361. if (!Rast_is_d_null_value(&supCell)) {
  362. if (Rast_is_d_null_value(&ptype) || supCell == ptype) {
  363. nedges++;
  364. }
  365. }
  366. }
  367. }
  368. else {
  369. /* exclude border edges */
  370. if (!Rast_is_d_null_value(&corrCell)) {
  371. area++;
  372. if (Rast_is_d_null_value(&ptype) || corrCell == ptype) {
  373. if (j > 0 && !(masked && mask_buf[j - 1] == 0) &&
  374. (Rast_is_d_null_value(&precCell) || precCell != corrCell)) {
  375. nedges++;
  376. }
  377. if (i > 0 && !(masked && mask_sup[i - 1] == 0) &&
  378. (Rast_is_d_null_value(&supCell) || supCell != corrCell)) {
  379. nedges++;
  380. }
  381. }
  382. }
  383. else if (Rast_is_d_null_value(&corrCell) && !(masked && mask_buf[j] == 0)) {
  384. if (!Rast_is_d_null_value(&precCell)) {
  385. if (Rast_is_d_null_value(&ptype) || precCell == ptype) {
  386. nedges++;
  387. }
  388. }
  389. if (!Rast_is_d_null_value(&supCell)) {
  390. if (Rast_is_d_null_value(&ptype) || supCell == ptype) {
  391. nedges++;
  392. }
  393. }
  394. }
  395. }
  396. precCell = corrCell;
  397. }
  398. }
  399. /* calculate index */
  400. if (area > 0) {
  401. double EW_DIST1, EW_DIST2, NS_DIST1, NS_DIST2;
  402. double elength, cell_size;
  403. /* calculate distance */
  404. G_begin_distance_calculations();
  405. /* EW Dist at North edge */
  406. EW_DIST1 = G_distance(hd.east, hd.north, hd.west, hd.north);
  407. /* EW Dist at South Edge */
  408. EW_DIST2 = G_distance(hd.east, hd.south, hd.west, hd.south);
  409. /* NS Dist at East edge */
  410. NS_DIST1 = G_distance(hd.east, hd.north, hd.east, hd.south);
  411. /* NS Dist at West edge */
  412. NS_DIST2 = G_distance(hd.west, hd.north, hd.west, hd.south);
  413. elength = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols) +
  414. (NS_DIST1 + NS_DIST2) / (2 * hd.rows)) / 2;
  415. cell_size = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols)) *
  416. ((NS_DIST1 + NS_DIST2) / (2 * hd.rows));
  417. *result = (double) nedges * elength * 10000 / (area * cell_size);
  418. }
  419. else
  420. Rast_set_d_null_value(result, 1);
  421. if (masked) {
  422. close(mask_fd);
  423. G_free(mask_buf);
  424. G_free(mask_sup);
  425. }
  426. G_free(buf_null);
  427. return RLI_OK;
  428. }
  429. int calculateF(int fd, struct area_entry *ad, char **par, double *result)
  430. {
  431. FCELL *buf, *buf_sup, *buf_null;
  432. FCELL corrCell, precCell, supCell;
  433. FCELL ptype;
  434. long nedges, area;
  435. int i, j;
  436. int mask_fd, *mask_buf, *mask_sup, *mask_tmp, masked;
  437. struct Cell_head hd;
  438. Rast_get_window(&hd);
  439. /* open mask if needed */
  440. mask_fd = -1;
  441. mask_buf = mask_sup = NULL;
  442. masked = FALSE;
  443. if (ad->mask == 1) {
  444. if ((mask_fd = open(ad->mask_name, O_RDONLY, 0755)) < 0)
  445. return RLI_ERRORE;
  446. mask_buf = G_malloc(ad->cl * sizeof(int));
  447. if (mask_buf == NULL) {
  448. G_fatal_error("malloc mask_buf failed");
  449. return RLI_ERRORE;
  450. }
  451. mask_sup = G_malloc(ad->cl * sizeof(int));
  452. if (mask_sup == NULL) {
  453. G_fatal_error("malloc mask_buf failed");
  454. return RLI_ERRORE;
  455. }
  456. for (j = 0; j < ad->cl; j++)
  457. mask_buf[j] = 0;
  458. masked = TRUE;
  459. }
  460. buf_null = Rast_allocate_f_buf();
  461. if (buf_null == NULL) {
  462. G_fatal_error("malloc buf_null failed");
  463. return RLI_ERRORE;
  464. }
  465. /* the first time buf_sup is all null */
  466. Rast_set_f_null_value(buf_null, Rast_window_cols());
  467. buf_sup = buf_null;
  468. if (par != NULL) { /* only 1 class */
  469. char *sval;
  470. sval = par[0];
  471. ptype = atof(sval);
  472. }
  473. else
  474. Rast_set_f_null_value(&ptype, 1);
  475. nedges = 0;
  476. area = 0;
  477. /* for each raster row */
  478. for (i = 0; i < ad->rl; i++) {
  479. /* read row of raster */
  480. buf = RLI_get_fcell_raster_row(fd, i + ad->y, ad);
  481. if (i > 0) /* not first row */
  482. buf_sup = RLI_get_fcell_raster_row(fd, i - 1 + ad->y, ad);
  483. /* read mask if needed */
  484. if (masked) {
  485. mask_tmp = mask_sup;
  486. mask_sup = mask_buf;
  487. mask_buf = mask_tmp;
  488. if (read(mask_fd, mask_buf, (ad->cl * sizeof(int))) < 0)
  489. return RLI_ERRORE;
  490. }
  491. Rast_set_f_null_value(&precCell, 1);
  492. for (j = 0; j < ad->cl; j++) {
  493. corrCell = buf[j + ad->x];
  494. if (masked && mask_buf[j] == 0) {
  495. Rast_set_f_null_value(&corrCell, 1);
  496. }
  497. supCell = buf_sup[j + ad->x];
  498. if (masked && (mask_sup[j] == 0)) {
  499. Rast_set_f_null_value(&supCell, 1);
  500. }
  501. if (brdr) {
  502. if (!Rast_is_f_null_value(&corrCell)) {
  503. area++;
  504. if (Rast_is_f_null_value(&ptype) || corrCell == ptype) {
  505. if (Rast_is_f_null_value(&precCell) || precCell != corrCell) {
  506. nedges++;
  507. }
  508. if (Rast_is_f_null_value(&supCell) || supCell != corrCell) {
  509. nedges++;
  510. }
  511. /* right and bottom */
  512. if (i == ad->rl - 1)
  513. nedges++;
  514. if (j == ad->cl - 1)
  515. nedges++;
  516. }
  517. }
  518. else /* corrCell == NULL */ {
  519. if (!Rast_is_f_null_value(&precCell)) {
  520. if (Rast_is_f_null_value(&ptype) || precCell == ptype) {
  521. nedges++;
  522. }
  523. }
  524. if (!Rast_is_f_null_value(&supCell)) {
  525. if (Rast_is_f_null_value(&ptype) || supCell == ptype) {
  526. nedges++;
  527. }
  528. }
  529. }
  530. }
  531. else {
  532. /* exclude border edges */
  533. if (!Rast_is_f_null_value(&corrCell)) {
  534. area++;
  535. if (Rast_is_f_null_value(&ptype) || corrCell == ptype) {
  536. if (j > 0 && !(masked && mask_buf[j - 1] == 0) &&
  537. (Rast_is_f_null_value(&precCell) || precCell != corrCell)) {
  538. nedges++;
  539. }
  540. if (i > 0 && !(masked && mask_sup[i - 1] == 0) &&
  541. (Rast_is_f_null_value(&supCell) || supCell != corrCell)) {
  542. nedges++;
  543. }
  544. }
  545. }
  546. else if (Rast_is_f_null_value(&corrCell) && !(masked && mask_buf[j] == 0)) {
  547. if (!Rast_is_f_null_value(&precCell)) {
  548. if (Rast_is_f_null_value(&ptype) || precCell == ptype) {
  549. nedges++;
  550. }
  551. }
  552. if (!Rast_is_f_null_value(&supCell)) {
  553. if (Rast_is_f_null_value(&ptype) || supCell == ptype) {
  554. nedges++;
  555. }
  556. }
  557. }
  558. }
  559. precCell = corrCell;
  560. }
  561. }
  562. /* calculate index */
  563. if (area > 0) {
  564. double EW_DIST1, EW_DIST2, NS_DIST1, NS_DIST2;
  565. double elength, cell_size;
  566. /* calculate distance */
  567. G_begin_distance_calculations();
  568. /* EW Dist at North edge */
  569. EW_DIST1 = G_distance(hd.east, hd.north, hd.west, hd.north);
  570. /* EW Dist at South Edge */
  571. EW_DIST2 = G_distance(hd.east, hd.south, hd.west, hd.south);
  572. /* NS Dist at East edge */
  573. NS_DIST1 = G_distance(hd.east, hd.north, hd.east, hd.south);
  574. /* NS Dist at West edge */
  575. NS_DIST2 = G_distance(hd.west, hd.north, hd.west, hd.south);
  576. elength = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols) +
  577. (NS_DIST1 + NS_DIST2) / (2 * hd.rows)) / 2;
  578. cell_size = ((EW_DIST1 + EW_DIST2) / (2 * hd.cols)) *
  579. ((NS_DIST1 + NS_DIST2) / (2 * hd.rows));
  580. *result = (double) nedges * elength * 10000 / (area * cell_size);
  581. }
  582. else
  583. Rast_set_d_null_value(result, 1);
  584. if (masked) {
  585. close(mask_fd);
  586. G_free(mask_buf);
  587. G_free(mask_sup);
  588. }
  589. G_free(buf_null);
  590. return RLI_OK;
  591. }