WSL/SLF GitLab Repository

PNGIO.cc 9.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***********************************************************************************/
/*  Copyright 2009 WSL Institute for Snow and Avalanche Research    SLF-DAVOS      */
/***********************************************************************************/
/* This file is part of MeteoIO.
    MeteoIO is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MeteoIO is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with MeteoIO.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "PNGIO.h"
#include <meteoio/ResamplingAlgorithms2D.h>
20
#include <meteoio/Graphics.h>
21
22
23
24
25
26
27
28
29
30

#include <algorithm>

using namespace std;

namespace mio {
/**
 * @page template PNGIO
 * @section template_format Format
 * *Put here the informations about the standard format that is implemented*
31
 * Finally, the naming scheme for meteo grids should be: YYYYMMDDHHmm_{MeteoGrids::Parameters}.png
32
33
34
35
36
37
38
39
40
41
42
43
 *
 * @section template_units Units
 *
 *
 * @section template_keywords Keywords
 * This plugin uses the following keywords:
 * - COORDSYS: coordinate system (see Coords); [Input] and [Output] section
 * - COORDPARAM: extra coordinates parameters (see Coords); [Input] and [Output] section
 * - etc
 */

const double PNGIO::plugin_nodata = -999.; //plugin specific nodata value. It can also be read by the plugin (depending on what is appropriate)
44
const double PNGIO::factor = 2.0; //image scale factor
45
const bool PNGIO::autoscale = true;
46
const bool PNGIO::has_legend = true;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

PNGIO::PNGIO(void (*delObj)(void*), const Config& i_cfg) : IOInterface(delObj), cfg(i_cfg)
{
	IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam);
}

PNGIO::PNGIO(const std::string& configfile) : IOInterface(NULL), cfg(configfile)
{
	IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam);
}

PNGIO::PNGIO(const Config& cfgreader) : IOInterface(NULL), cfg(cfgreader)
{
	IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam);
}

PNGIO::~PNGIO() throw()
{

}

68
69
70
71
72
73
74
void PNGIO::read2DGrid(Grid2DObject&, const std::string&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::read2DGrid(Grid2DObject&, const MeteoGrids::Parameters& , const Date&)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readDEM(DEMObject& /*dem_out*/)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readLanduse(Grid2DObject& /*landuse_out*/)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readAssimilationData(const Date& /*date_in*/, Grid2DObject& /*da_out*/)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readStationData(const Date&, std::vector<StationData>& /*vecStation*/)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readMeteoData(const Date& /*dateStart*/, const Date& /*dateEnd*/,
                             std::vector< std::vector<MeteoData> >& /*vecMeteo*/,
                             const size_t&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::writeMeteoData(const std::vector< std::vector<MeteoData> >& /*vecMeteo*/,
                              const std::string&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::readSpecialPoints(std::vector<Coords>&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void PNGIO::write2DGrid(const Grid2DObject& grid_in, const std::string& filename)
{
	FILE *fp;
	png_structp png_ptr=NULL;
	png_infop info_ptr=NULL;
	png_bytep row=NULL;

	//scale input image
	Grid2DObject grid = ResamplingAlgorithms2D::BilinearResampling(grid_in, factor);
	const double ncols = grid.ncols, nrows = grid.nrows;
135
136
	const double min = grid.grid2D.getMin();
	const double max = grid.grid2D.getMax();
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

	// Open file for writing (binary mode)
	if (!IOUtils::validFileName(filename)) {
		throw InvalidFileNameException(filename, AT);
	}
	fp = fopen(filename.c_str(), "wb");
	if (fp == NULL) {
		throw FileAccessException(filename, AT);
	}

	// Initialize write structure
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		fclose(fp);
		throw IOException("Could not allocate write structure", AT);
	}

	// Initialize info structure
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		fclose(fp);
		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
		throw IOException("Could not allocate info structure", AT);
	}

	// Setup Exception handling
	if (setjmp(png_jmpbuf(png_ptr))) {
		cleanup(fp, png_ptr, info_ptr, row);
		throw IOException("Error during png creation", AT);
	}

	png_init_io(png_ptr, fp);

170
171
172
173
174
175
176
177
178
	unsigned int full_width=ncols;
	Array2D<double> legend_array;
	if(has_legend) {
		legend leg(nrows, min, max);
		legend_array = leg.getLegend();
		unsigned int nx, ny;
		legend_array.size(nx,ny);
		full_width += nx;
	}
179
	// Write header (8 bit colour depth)
180
	png_set_IHDR(png_ptr, info_ptr, full_width, nrows,
181
182
	             8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
	             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
183
	writeMetadata(grid, png_ptr, info_ptr);
184
185
186
	png_write_info(png_ptr, info_ptr);

	// Allocate memory for one row (4 bytes per pixel - RGBA)
187
	row = (png_bytep) malloc(4 * full_width * sizeof(png_byte));
188
189

	// Write image data
190
	Gradient gradient(Gradient::heat, min, max);
191
192
	for(int y=nrows-1 ; y>=0 ; y--) {
		for(unsigned int x=0 ; x<ncols ; x++) {
193
194
195
196
			const unsigned int i=x*4;
			unsigned char r,g,b,a;
			gradient.getColor(grid(x,y), r,g,b,a);
			row[i]=r; row[i+1]=g; row[i+2]=b; row[i+3]=a;
197
198
		}
		for(unsigned int x=ncols; x<full_width; x++) {
199
200
201
202
203
			//setRGB( legend_array(x-ncols,y), min, max, &(row[x*4]) );
			const unsigned int i=x*4;
			unsigned char r,g,b,a;
			gradient.getColor(legend_array(x-ncols,y), r,g,b,a);
			row[i]=r; row[i+1]=g; row[i+2]=b; row[i+3]=a;
204
205
206
207
208
209
210
211
		}
		png_write_row(png_ptr, row);
	}

	png_write_end(png_ptr, NULL);
	cleanup(fp, png_ptr, info_ptr, row);
}

212
213
214
215
216
217
218
void PNGIO::write2DGrid(const Grid2DObject& grid_in, const MeteoGrids::Parameters& parameter, const Date& date)
{
	std::stringstream ss;
	ss << date.toString(Date::NUM) << "_" << MeteoGrids::getParameterName(parameter) << ".png";
	write2DGrid(grid_in, ss.str());
}

219
220
221
222
223
void PNGIO::cleanup() throw()
{

}

224
void PNGIO::writeMetadata(const Grid2DObject& grid, png_structp &png_ptr, png_infop &info_ptr)
225
226
227
228
229
{
	const std::string version = "MeteoIO "+getLibVersion();
	char version_c[79]=""; strncpy(version_c, version.c_str(), 79);
	const std::string logname = IOUtils::getLogName();
	char logname_c[79]=""; strncpy(logname_c, logname.c_str(), 79);
230
231
232
233
234
	char latitude[12]=""; snprintf(latitude, 12, "%.6f", grid.llcorner.getLat());
	char longitude[12]=""; snprintf(longitude, 12, "%.6f", grid.llcorner.getLon());
	char cellsize[12]=""; snprintf(cellsize, 12, "%.2f", (grid.cellsize*factor)); //HACK: not working...
	
	png_text info_text[7];
235
	info_text[0].key = (char*)"Title";
236
	info_text[0].text = (char*)"Gridded data"; //HACK: write meteogrid parameter
237
238
239
240
241
242
243
	info_text[0].compression = PNG_TEXT_COMPRESSION_NONE;
	info_text[1].key = (char*)"Author";
	info_text[1].text = logname_c;
	info_text[1].compression = PNG_TEXT_COMPRESSION_NONE;
	info_text[2].key = (char*)"Software";
	info_text[2].text = version_c;
	info_text[2].compression = PNG_TEXT_COMPRESSION_NONE;
244
245
246
247
248
249
250
251
252
253
254
255
	info_text[3].key = (char*)"Position"; //HACK: see exif key
	info_text[3].text = (char*)"llcorner";
	info_text[3].compression = PNG_TEXT_COMPRESSION_NONE;
	info_text[4].key = (char*)"Latitude"; //HACK: see exif key
	info_text[4].text = latitude;
	info_text[4].compression = PNG_TEXT_COMPRESSION_NONE;
	info_text[5].key = (char*)"Longitude";
	info_text[5].text = longitude;
	info_text[5].compression = PNG_TEXT_COMPRESSION_NONE;
	info_text[6].key = (char*)"Cellsize";
	info_text[6].text = cellsize;
	info_text[6].compression = PNG_TEXT_COMPRESSION_NONE;
256
257
	//TODO: add geolocalization tags: latitude, longitude, altitude, cellsize + indicator of position: llcorner
	//add data set timestamp
258
	png_set_text(png_ptr, info_ptr, info_text, 7);
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
}

void PNGIO::cleanup(FILE *fp, png_structp png_ptr, png_infop info_ptr, png_bytep row)
{
	if (fp != NULL) fclose(fp);
	if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
	if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
	if (row != NULL) free(row);
}

#ifndef _METEOIO_JNI
extern "C"
{
#define COMPILE_PLUGIN
#include "exports.h"

	METEOIO_EXPORT void deleteObject(void* obj) {
		delete reinterpret_cast<PluginObject*>(obj);
	}

	METEOIO_EXPORT void* loadObject(const string& classname, const Config& cfg) {
		if(classname == "PNGIO") {
			//cerr << "Creating dynamic handle for " << classname << endl;
			return new PNGIO(deleteObject, cfg);
		}
		//cerr << "Could not load " << classname << endl;
		return NULL;
	}
}
#endif

} //namespace