]> www.wagner.pp.ru Git - oss/fgis.git/blob - lib/epp_output.c
First checked in version
[oss/fgis.git] / lib / epp_output.c
1 # define _POSIX_SOURCE
2 /* I don't know what it means, but otherwise fileno function is not declared
3    in stdio.h
4  */
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 # define __USE_MISC
9 /* Even more strange define. It is neccesary to access ftruncate function */
10 #include <unistd.h>
11 # undef __USE_MISC
12 #include <epp.h>
13 # include <math.h>
14 # include <time.h>
15 # include "epp_private.h"
16 int Create16bit=0; /* if non-zero, all created EPP files would be 16 bit*/
17
18 EPP *creat_epp(char *pathname,int first_col,int first_row,int last_col,
19                int last_row, double AXLeft,double AYTop, double AXRight,
20                double AYBottom, int scale, int base, int offsite)
21 /* creating epp file from scratch. All fields which are not defined as
22  parameters, are filled by default values. Kind depends of global variable
23  Create16bit */
24 { FILE *f;
25   f=fopen(pathname,"wb+");
26   if (f==NULL) {map_error=ME_CREATE_ERROR;return NULL;}
27   return fcreat_epp(f,first_col,first_row,last_col,last_row,AXLeft,AYTop,
28   AXRight,AYBottom,scale,base,offsite);
29 }  
30
31
32 EPP *fcreat_epp(FILE* f,int first_col,int first_row,int last_col,
33                int last_row, double AXLeft,double AYTop, double AXRight,
34                double AYBottom, int scale, int base, int offsite)
35
36 { EPPHEADER h;
37   EPP *epp;
38   int i;
39   unsigned short int *r;
40   memset(&h,128,0);
41   if ((epp=malloc(sizeof(EPP)))==NULL) {fclose(f);map_error=ME_OUT_OF_MEMORY;
42                                            return NULL;}
43   h.fcx=swapdouble(epp->XLeft=AXLeft);
44   h.lcx=swapdouble(epp->XRight=AXRight);
45   h.lry=swapdouble(epp->YBottom=AYBottom);
46   h.fry=swapdouble(epp->YTop=AYTop);
47   h.fc=swapshort(epp->fc=first_col);
48   h.fr=swapshort(epp->fr=first_row);
49   h.lc=swapshort(epp->lc=last_col);epp->lc++;
50   h.lr=swapshort(epp->lr=last_row);epp->lr++;
51   h.scale=swapshort(scale==0?100:scale);
52   h.base=swapshort(base);
53   h.offsite=swapshort(epp->offsite=offsite);
54   epp->width=last_col-first_col+2;
55   if ((epp->row=calloc(epp->width+1,sizeof(short)))==NULL)
56    {free(epp);fclose(f);map_error=ME_OUT_OF_MEMORY;return NULL;}
57   epp->min=65535;
58   epp->max=0;
59   for(r=epp->row,i=0;i<=epp->width;*(r++)=offsite,i++);
60   if((epp->widthtable=calloc(epp->lr-epp->fr,sizeof(short)))==NULL)
61    {free(epp->row);free(epp);fclose(f);map_error=ME_OUT_OF_MEMORY;return NULL;}
62   if ((epp->packed_buffer=malloc(epp->width*4/3))==NULL)
63   { free(epp->row);if (epp->widthtable!=NULL) free(epp->widthtable);
64     fclose(f);free(epp);map_error=ME_OUT_OF_MEMORY;return NULL;}
65   epp->F=f;
66   epp->mode=MAP_OUTPUT;
67   epp->position=position_output;
68   h.kind=swapshort(epp->kind=Create16bit?16:8);
69   h.access_ptr=0;
70   h.sfact=swapdouble(epp->cell_area=fabs(AXRight-AXLeft)/(last_col-first_col+1)*
71     fabs(AYBottom-AYTop)/(last_row-first_row+1));
72   memset(h.comment,32,' ');
73   fwrite(&h,128,1,f);
74   epp->filepos=128;
75   epp->currentline=epp->fr;
76   epp->cache_size=0;
77   epp->cache=NULL;
78   return epp;
79 }
80 EPP *creat_epp_as(char *pathname,EPP *pattern)
81 { FILE *f;
82   f=fopen(pathname,"wb+");
83   if (f==NULL) {map_error=ME_CREATE_ERROR;return NULL;}
84   return fcreat_epp_as(f,pattern);
85 }  
86 EPP *fcreat_epp_as(FILE *f,EPP *pattern)
87 /* creates new epp file, copiing a most header fields from given file */
88 { EPPHEADER h;
89   EPP *epp;
90   get_epp_header(pattern,&h);
91   epp=fcreat_epp(f,h.fc,h.fr,h.lc,h.lr,
92                 pattern->XLeft,pattern->YTop,pattern->XRight,
93                 pattern->YBottom,h.scale,h.base,h.offsite);
94   if (epp!=NULL)
95    { char cmt[33];
96      setcomment(epp,strncpy(cmt,h.comment,32));
97    }
98   return epp;
99 }
100 void setcomment(EPP *epp,char *comment)
101 /* change comment of epp file, which was open by creat_epp or creat_epp_as */
102 { EPPHEADER h;
103   int i;
104  if((epp->mode & MAP_OUTPUT) == 0)
105   { map_error=ME_INVALID_MODE; return; }
106  fseek(epp->F,0L,SEEK_SET);
107  fread(&h,128,1,epp->F);
108  strncpy(h.comment,comment,32); 
109  for(i=strlen(comment);i<32;i++) h.comment[i]=' ';
110  fseek(epp->F,0L,SEEK_SET);
111  fwrite(&h,128,1,epp->F);
112  fseek(epp->F,epp->filepos,SEEK_SET);
113 }
114 void change_epp_header(EPP *epp,EPPHEADER h)
115
116   EPPHEADER fh;
117   fseek(epp->F,0L,SEEK_SET);
118   fread(&fh,128,1,epp->F);
119   fh.fcx=swapdouble(h.fcx);
120   fh.lcx=swapdouble(h.lcx);
121   fh.fry=swapdouble(h.fry);
122   fh.lry=swapdouble(h.lry);
123   fh.base=swapshort(h.base);
124   fh.scale=swapshort(h.scale);
125   fh.offsite=swapshort(epp->offsite=h.offsite);
126   fh.sfact=swapdouble(h.sfact);
127   fh.coord_sys=h.coord_sys;
128   fh.area_unit=h.area_unit;
129   memcpy(h.comment,fh.comment,32);
130   fseek(epp->F,0L,SEEK_SET);
131   fwrite(&fh,128,1,epp->F);
132   fseek(epp->F,epp->filepos,SEEK_SET);
133 }
134
135 int shift_epp(EPP *epp,int new_fr,int new_fc)
136 {int new_lr,new_lc,result;
137  EPPHEADER h;
138  new_lr=epp->lr-epp->fr+new_fr;
139  new_lc=epp->lc-epp->fc+new_fc;
140  if ((new_fr<-32767)||(new_fc<-32767)||(new_lc>32768)||(new_lc>32768))
141   return 0;
142  if (epp->mode & MAP_OUTPUT)
143  { 
144  fseek(epp->F,0,SEEK_SET);
145  fread(&h,128,1,epp->F);
146  h.fc=swapshort(epp->fc=new_fc);
147  h.fr=swapshort(epp->fr=new_fr);
148  h.lr=swapshort((epp->lr=new_lr)-1);
149  h.lc=swapshort((epp->lc=new_lc)-1);
150  fseek(epp->F,0,SEEK_SET);
151  result=(fwrite(&h,128,1,epp->F)==128);
152  fseek(epp->F,epp->filepos,SEEK_SET);
153  }
154  else  result=1;
155  if (result)
156  { epp->fr=new_fr;
157    epp->lr=new_lr;
158    epp->fc=new_fc;
159    epp->lc=new_lc;
160  }
161  return result; 
162 }
163     
164
165 void reset_epp(EPP *epp)
166 /* reopens epp file for reading. if file was in create mode, fills all non-filled
167 lines by offsite */
168 { if (epp->mode==MAP_OUTPUT)
169   { update_header(epp); 
170     epp->mode=MAP_INPUT;
171     fseek(epp->F,128L,SEEK_SET);
172     epp->filepos=128L;
173     epp->currentline=epp->fr-1;
174     epp->position=position_input;
175    }
176    else (*epp->position)(epp,epp->fr);   
177 }
178 /*
179  * update_header - writes changes into header and writes width table.
180  * for write-only files also fills all unfilled rows by offsite.
181  * Modifies following field in file header:
182  * 1. access_ptr (to point to newly written width table)
183  * 2. maxclass
184  * 3. minclass
185  * 4. offsite (so we can play with its value)
186  * 5. date and time stamp
187  */
188 void update_header(EPP *epp)/* writes changes into header */
189
190   int i;
191   unsigned short int *r;
192   struct tm *t;
193   int table_offset;
194 #ifndef LSB_FIRST
195   short *fwt;
196 #endif
197   time_t timesec;
198   EPPHEADER h;
199   /* For write-only file fill rest of it by offsite */
200   if(epp->mode==MAP_OUTPUT) { 
201     put_row(epp);
202     for (r=epp->row,i=0;i<epp->width;*(r++)=epp->offsite,i++);
203     for (i=epp->currentline;i<epp->lr;i++){put_row(epp);}
204   }  
205   memset(&h,128,0);
206   /* width table should be aligned to 128 byte boundary */
207   if((i=(epp->filepos % 128))) fwrite(&h,128-i,1,epp->F);
208   table_offset=ftell(epp->F);
209 #ifdef LSB_FIRST
210   fwrite(epp->widthtable,epp->lr-epp->fr,sizeof(short),epp->F);
211 #else
212   fwt=calloc(epp->lr-epp->fr,sizeof(short));
213   swab(epp->widthtable,fwt,epp->lr-epp->fr);
214   fwrite(fwt,epp->lr-epp->fr,sizeof(short),epp->F);
215   free(fwt);
216 #endif   
217   if ((i=(ftell(epp->F)%128))) fwrite(&h,128-i,1,epp->F);
218   /* I don't know how old software would behave if file size is
219      not multiplication of 128, so write few bytes of junk */
220   if ((i=(ftell(epp->F)%128))) fwrite(&h,128-i,1,epp->F);
221   /* Truncate file here.*/
222   ftruncate(fileno(epp->F),ftell(epp->F));
223   /* Now we are ready to write header. */
224   fseek(epp->F,0,SEEK_SET);
225   fread(&h,128,1,epp->F);
226   h.access_ptr=swaplong(table_offset / 128);
227   h.offsite=swapshort(epp->offsite);
228   h.maxclass=swapshort(epp->max);
229   h.minclass=swapshort(epp->min);
230   /* correct date/time values */
231   time(&timesec);
232   t=localtime(&timesec);
233   sprintf(h.date,"%02d/%02d/%02d",t->tm_mon+1,t->tm_mday,t->tm_year);
234   for(i=strlen(h.date);i<16;h.date[i++]=' ');
235   sprintf(h.time,"%02d:%02d:%02d",t->tm_hour,t->tm_min,t->tm_sec);
236   for(i=strlen(h.time);i<8;h.time[i++]=' ');
237   fseek(epp->F,0L,SEEK_SET);
238   fwrite(&h,128,1,epp->F);
239   epp->filepos=128;
240   epp->currentline=epp->fr-1;
241 }
242 /*
243  * Perform actual eppl packing algoritm. Fetch each second byte from
244  * source row, so pack_buffer can call it twice for 16-bit files
245  * row is row buffer to pack (may be pointer to second byte of actual
246  * row buffer) bufpos - stores offset in epp->packed_buffer, where to
247  * start packing. On exit it contains offset of next byte after last packed
248  */
249
250 void pack_row(unsigned char *row,int *bufpos,EPP *epp)
251 {
252   register unsigned char c, *dest, *i;
253   register unsigned char *stop;
254   register unsigned char *j, *k;
255   stop=row+((epp->width-1)<<1);
256   dest=epp->packed_buffer+(*bufpos);
257   for(i=row;i<stop;)
258    { j=i+2;
259      c=1;
260      while((j<stop)&&(c<255)&&(*i==*j))
261       { j+=2;c++;}
262      if (c>1)
263       { *(dest++)=c;*(dest++)=*i;
264         i=j;
265       }
266      else
267       { for(j=i+2;(j<stop-2)&&(c<255)&&(*j!=*(j+2));
268             j+=2,c++);
269         if ((j==stop-2)&&(c<255)) {j+=2;c++;}
270         if (c>1)
271           { *(dest++)=0;}
272         *(dest++)=c;
273         for (k=i;k<j;k+=2) *(dest++)=*k;
274         i=j;
275       }
276   }
277   *bufpos=(int)dest-(int)(epp->packed_buffer);
278 }
279 /*
280  * Stores content of epp->row into epp->packed_buffer by calling
281  * pack_row epp->kind/8 times.
282  * Updates width table.
283  */
284 int pack_buffer(EPP *epp)
285 {int bufpos=0;
286  if (!(epp->mode & MAP_OUTPUT)) {map_error=ME_INVALID_MODE; return 0;}
287 #ifdef LSB_FIRST
288   /* Intel architecture - first byte of short is LSB. store it first */
289   pack_row((unsigned char *)epp->row,&bufpos,epp);
290   if (epp->kind==16)
291       pack_row(((unsigned char *)epp->row)+1,&bufpos,epp);
292 #else
293   /* Sparc architecture - store second byte of short first, first byte last*/
294   pack_row(((unsigned char *)epp->row)+1,&bufpos,epp);
295   if (epp->kind==16)
296       pack_row(((unsigned char *)epp->row),&bufpos,epp);
297 #endif
298   /* update width table */
299   epp->widthtable[epp->currentline-epp->fr]=bufpos;
300   return bufpos;
301
302 /*
303  * Packs and writes row
304  *
305  */
306 void put_row(EPP *epp)
307 {   int l;
308     if (!(epp->mode & MAP_OUTPUT)) {
309         map_error=ME_INVALID_MODE; 
310         return;
311     }
312     l=pack_buffer(epp);
313     if (fwrite(epp->packed_buffer,1,l,epp->F)!=l) {
314         map_error=ME_WRITE_ERROR;
315         return;
316     }
317     epp->filepos+=l;
318     epp->currentline++; 
319 }
320 /*
321  * Positions write-only file. To be stored in position field of EPP structure.
322  * If row > current fills all rows between current and given by offsite.
323  */ 
324 void position_output(EPP *epp,int row)
325
326   unsigned short *r;
327   int i;
328   
329   if (row==epp->currentline) 
330      return;
331   if (!epp_contains(epp,epp->fc,row)) {
332       map_error=ME_POINT_OUTSIDE; return;
333   }
334   if (row<epp->currentline) { 
335      map_error=ME_INVALID_PUT; 
336      return;
337   }
338   /* store current row */
339   map_error=0;
340   put_row(epp);
341   if (map_error) return;
342   /* fill row buffer with offsite */
343   for (r=epp->row,i=epp->fc;i<epp->lc;i++,r++)*r=epp->offsite;
344   while (epp->currentline<row) {
345      put_row(epp); 
346      if (map_error) return;
347   }
348   /* Here we are - buffer filled with offsite and prevouis rows are written */
349 }
350 /*
351  * Recieves write-only file, writing to which is just finished,
352  *  and writes it into filename as
353  * 8-bit file.
354  * Doesn't even try to unpack high byte
355  */
356 void fast_convert_to_8bit(EPP *source,char *filename)
357 {
358    int bufpos;
359    FILE *dest;
360 #ifndef LSB_FIRST
361    short *swapped_bytes;
362 #endif
363    EPPHEADER h;
364    int i,j,nrows,access_ptr;
365    if (source->mode!=MAP_OUTPUT) {
366       map_error=ME_INVALID_MODE; 
367       return;
368    }
369  reset_epp(source);
370  fseek(source->F,0L,SEEK_SET);
371  fread(&h,128,1,source->F);
372  h.kind=swapshort(8);
373  if (source->offsite>255&&source->offsite!=65535) {
374      h.offsite=swapshort(source->offsite & 0xff);
375  }
376  dest=fopen(filename,"w+");
377  if (!dest) {
378      map_error=ME_WRITE_ERROR;
379      return;
380  }
381  fwrite(&h,128,1,dest);
382  nrows=source->lr-source->fr;
383  for(i=source->fr,j=0;i<source->lr;i++,j++) { 
384    if (EndLineProc) 
385       (*EndLineProc)(i,j+1,nrows);
386    fread(source->packed_buffer,source->widthtable[j],1,source->F);
387    bufpos=0;
388    unpack_row((unsigned char *)source->row,&bufpos,source);
389    source->widthtable[j]=bufpos;
390    fwrite(source->packed_buffer,bufpos,1,dest); 
391   }
392   memset(&h,128,0);
393   /* This is assignment, not condition */
394   if((i=ftell(dest) % 128)) fwrite(&h,128-i,1,dest);
395   access_ptr=ftell(dest)/128;
396 #ifdef LSB_FIRST
397   fwrite(source->widthtable,nrows,2,dest);
398 #else
399   swapped_bytes=malloc(nrows*2);
400   swab(source->widthtable,swapped_bytes,nrows*2);
401   fwrite(swapped_bytes,nrows,2,dest);
402 #endif
403   fseek(dest,0L,SEEK_SET);
404   fread(&h,128,1,dest);
405   h.access_ptr=swaplong(access_ptr);
406   fseek(dest,0L,SEEK_SET);
407   fwrite(&h,128,1,dest);
408   fclose(dest);
409   close_epp(source);
410 }
411
412 /* fills given cell by value. File must be in create mode. if line is not
413  current, does nothing if line above current or fills all lines between current
414  and specified by offsite 
415 */
416 void epp_put(EPP *epp,int x,int y,int value)
417 {  
418     unsigned short *pixel;
419
420     if (!(epp->mode&MAP_OUTPUT)){
421         map_error=ME_INVALID_MODE; return ;
422     }
423
424     if (!epp_contains(epp,x,y)) {
425         map_error=ME_POINT_OUTSIDE; return;
426     }
427
428     if (epp->currentline!=y) { 
429     (*epp->position)(epp,y);
430     }
431     if (epp->counttable) {
432     /* if we are supporting counting of cell values*/
433         pixel=epp->row+(x-epp->fc);
434         /* # maintain table */
435         epp->counttable[*pixel]--;
436         epp->counttable[value]++;
437         *pixel=value;
438        /* # check if maximum or minimum changed   */
439         if (value>epp->max&&value!=epp->offsite) {
440            epp->max=value;
441         } else {
442            while ((epp->max==epp->offsite||!epp->counttable[epp->max])&&
443                     epp->max) 
444               epp->max--;
445         }  
446         if (value<epp->min&&value!=epp->offsite) {
447            epp->min=value;
448         } else {
449            while ((epp->min==epp->offsite||!epp->counttable[epp->min])&&
450                   epp->min<epp->max)
451               epp->min++;
452         }
453     } else {
454      /* we can only change max/min if value is outside them */
455         epp->row[x-epp->fc]=value;
456         if (value!=epp->offsite) {
457             if ((unsigned)value>epp->max) epp->max=value;
458             if ((unsigned)value<epp->min) epp->min=value;
459         }
460     }
461     epp->modified=1;
462 }
463 /* fills range of adjanced cells in same row with same value 
464    In create mode complains, if row is above current.
465    If any point of specified range is outside physical file limits,
466    complains and does nothing.
467 */
468 void epp_putline(EPP *epp,int x1,int x2,int y,int value)
469 {  
470     unsigned short *pixel;
471     int i;
472     if (!(epp->mode&MAP_OUTPUT)){
473         map_error=ME_INVALID_MODE; return ;
474     }
475
476     map_error=0;
477     /* Positioning to desired row */
478     if (epp->currentline!=y) { 
479     (*epp->position)(epp,y);
480     }
481     /* return if row is invalid */
482     if (map_error) return;
483     /* if line exceeds file limits, truncate it */
484     if (x1<epp->fc) x1=epp->fc;
485     if (x2>=epp->lc) x2=epp->lc-1;
486     /* if nothing left, return error */
487     if (x1>x2) {
488        map_error=ME_POINT_OUTSIDE;
489        return;
490     }
491     if (epp->counttable) {
492     /* if we are supporting counting of cell values*/
493         for (pixel=epp->row+(x1-epp->fc),i=x1;i<=x2;i++,pixel++) {
494            /* # maintain table */
495            epp->counttable[*pixel]--;
496            *pixel=value;
497         }
498         epp->counttable[value]+=x2-x1+1;
499        /* # check if maximum or minimum changed   */
500         if (value>epp->max&&value!=epp->offsite) {
501            epp->max=value;
502         } else {
503            while ((epp->max==epp->offsite||!epp->counttable[epp->max])&&
504                     epp->max) 
505               epp->max--;
506         }  
507         if (value<epp->min&&value!=epp->offsite) {
508            epp->min=value;
509         } else {
510            while ((epp->min==epp->offsite||!epp->counttable[epp->min])&&
511                   epp->min<epp->max)
512               epp->min++;
513         }
514     } else {
515      /* we can only change max/min if value is outside them */
516         for (pixel=epp->row+(x1-epp->fc),i=x1;i<=x2;i++,pixel++) {  
517            *pixel=value;
518         }
519         if (value!=epp->offsite) {
520             if ((unsigned)value>epp->max) epp->max=value;
521             if ((unsigned)value<epp->min) epp->min=value;
522         }
523     }
524     epp->modified=1;
525 }