Exemplo n.º 1
0
bool IGN_ReadTiles(long minx, long maxx, long miny, long maxy, CProgressWindow& wndProgress, 
						unsigned char * bitmap_palette, long & bitmap_palette_length, 
						unsigned char * & bitmap_memory , long & bitmap_memory_size, 
						long & bitmap_width, long & dest_bitmap_width,
						long & bitmap_height, long & dest_bitmap_height)
{
	// PNG writing variables
CIGNTileDatabase	db;

	long width=400, height=400;

	long x,y;
	for (y=maxy-1; y>=miny; y--) {
		for (x=minx; x<maxx; x++) {

			if (!wndProgress.ProgressBar()) return false;

// COMMON start
			if (bitmap_memory == NULL) {
				// First square found so allocate memory for bitmap
				bitmap_width = width*(maxx-minx);
				bitmap_height = height*(maxy-miny);
				dest_bitmap_width = width*(maxx-minx-2);
				dest_bitmap_height = height*(maxy-miny-2);

				bitmap_memory_size = bitmap_width * bitmap_height;
				bitmap_memory = (BYTE *)malloc(bitmap_memory_size);
				if (bitmap_memory == NULL) {
					printf("Couldn't allocate %d bytes for bitmap\n", bitmap_memory_size);
					//exit(0);
					return false;
				}
				memset(bitmap_memory, 0x00, bitmap_memory_size);		// assume 0x00 is white

/*
				if (bitmap_width > 32000)
					printf("Warning: width is %d pixels\n", bitmap_width);
				if (bitmap_height > 32000)
					printf("Warning: height is %d pixels\n", bitmap_height);
*/

				// get palette from database
				bitmap_palette_length = 4*256;
				memcpy(bitmap_palette, get_ign_palette(), bitmap_palette_length);
			}
// COMMON end
			// copy tile into bitmap area
			IGN_read_bmp(y*1000, x*1000, &db, bitmap_memory + (maxy-1-y)*height*bitmap_width+(x-minx)*width, bitmap_width);
		}
	}
	return true;
}
Exemplo n.º 2
0
int wmsLoadTiles(CTileDatabase* g_db, long minx, long miny, long maxx, long maxy, BOOL bForce, int nMapScale)
{
HINTERNET hOpen, hConnect;
char strTile[256];

	hOpen = NULL;
	FILE * fp_log = fopen("log.txt", "a+");

CProgressWindow wndProgress;
wndProgress.Initialize();

int nMeters = MyMap.GetMetresPerTile();

wndProgress.ResetProgressBar("Downloading:", (maxy-miny)/nMeters*(maxx-minx)/nMeters);

	// Grab in (8 x 8????) tiles.
	for (long y=miny; y<maxy; y+=nMeters) {
		for (long x=minx; x<maxx; x+=nMeters) {

			if (!wndProgress.ProgressBar()) return false;

			if (!bForce && g_db->TileLoaded(y, x)) {
		
				fprintf(fp_log, "Tile: [%05d,%05d] skipped - tile exists\n", x,y);
				continue;
			} else {
				
				// Only connect to WMS if required.
// SNL 11/06/2013 - hmmmm!
CString str1 = MyMap.wmsGetAttribution();
int port = MyMap.wmsGetPort();
				g_db->InitDatabase(y,x,MyMap.GetDatum());
				if (hOpen == NULL) {
					if ( !(hOpen = InternetOpen ( "Sample",  LOCAL_INTERNET_ACCESS , NULL, 0, 0) ) ) {
						ErrorOut ( GetLastError(), "InternetOpen");
						return 0;
					}

					if ( !(hConnect = InternetConnect ( hOpen, MyMap.wmsGetAttribution(), MyMap.wmsGetPort(), "",	"", INTERNET_SERVICE_HTTP, 0  , 0) ) ) {
						ErrorOut (GetLastError(), "InternetConnect");
						return 0;
					}
				}
				fprintf(fp_log, "Tile: [%05d,%05d] loading...\n", x,y);
				wmsGetTile(hConnect, strTile, y, x, g_db, fp_log, bForce, nMapScale);
			}

			if (abortProgram) {
				y=miny;
				x=maxx;
			}
		}
	}
	fclose(fp_log);

	if (hOpen != NULL) {
		if (!InternetCloseHandle (hConnect) ) {
			ErrorOut (GetLastError (), "CloseHandle on hConnect");
			return FALSE;
		}
		if (!InternetCloseHandle (hOpen) ) {
			ErrorOut (GetLastError (), "CloseHandle on hOpen");
			return FALSE;
		}
	}
	return 0;
}
Exemplo n.º 3
0
bool IGN_Create_PNG_JPR(LPCSTR mapname, long minx, long miny, long maxx, long maxy)
{
char tstring[256];
long bitmap_width = TILE_WIDTH*(maxx-minx)/1000;
long bitmap_size = bitmap_width*TILE_WIDTH;
unsigned char * bitmap_memory = (unsigned char *)malloc(bitmap_size);
CIGNTileDatabase	db;


	if (bitmap_memory == NULL) {
		printf("Couldn't allocate %d bytes of memory\n");
		return false;
	}


	// Create PNG header and write out palette
	sprintf(tstring, "%s.png", mapname);
	FILE * fp_png = fopen(tstring, "wb");
	if (fp_png == NULL) {
		printf("Couldn't open %s\n", tstring);
		return false;
	}
	
	// Create and initialize the png_struct with the desired error handler functions.
	png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		fclose(fp_png);
		return false;
	}
	
	/* Allocate/initialize the image information data.  REQUIRED */
	png_infop info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
		fclose(fp_png);
		return false;
	}
	
	/* Set error handling.  REQUIRED */
	if (setjmp(png_jmpbuf(png_ptr))) {
		/* If we get here, we had a problem reading the file */
		png_destroy_write_struct(&png_ptr, &info_ptr);
		fclose(fp_png);
		return false;
	}

	/* set up the output control if you are using standard C streams */
	png_init_io(png_ptr, fp_png);
	
	/* Set the image information here.  Width and height are up to 2^31,
	 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
	 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
	 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
	 * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
	 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
	 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
	 */
	png_set_IHDR(png_ptr, info_ptr, TILE_WIDTH*(maxx-minx)/1000, TILE_WIDTH*((maxy-miny)/1000), 8, PNG_COLOR_TYPE_PALETTE,
				PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

	/* set the palette if there is one.  REQUIRED for indexed-color images */
	png_colorp png_palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color));


	int i;
	BYTE * pal_ptr = (BYTE *)png_palette;
	BYTE * palette = ign_palette;
	for (i=0; i<256; i++) {
		*pal_ptr++ = palette[i*4 + 2];
		*pal_ptr++ = palette[i*4 + 1];
		*pal_ptr++ = palette[i*4 + 0];
	}

	/* ... set palette colors ... */
	png_set_PLTE(png_ptr, info_ptr, png_palette, PNG_MAX_PALETTE_LENGTH);
	/* You must not free palette here, because png_set_PLTE only makes a link to
	 * the palette that you malloced.  Wait until you are about to destroy
	 *the png structure. */
	
	/* Write the file header information.  REQUIRED */
	png_write_info(png_ptr, info_ptr);

CProgressWindow wndProgress;
wndProgress.Initialize();

wndProgress.ResetProgressBar("Tile:", (maxy-miny)/500*(maxx-minx)/500);


	for (int y=maxy-1000; y>=miny; y-=1000) {
		memset(bitmap_memory, 0x00, bitmap_size);
		for (int x=minx; x<maxx; x+=1000) {
			IGN_read_bmp(y, x, &db, bitmap_memory+(x-minx)/1000*TILE_WIDTH, bitmap_width);
		}


		// write row of PNG to file
		for (int x=0; x<TILE_WIDTH; x++) {
			png_write_row(png_ptr, bitmap_memory + x*bitmap_width);

			if (!wndProgress.ProgressBar()) return false;
		}
	}
	/* It is REQUIRED to call this to finish writing the rest of the file */
	png_write_end(png_ptr, info_ptr);

	png_free(png_ptr, png_palette);
	
	/* clean up after the write, and free any memory allocated */
	png_destroy_write_struct(&png_ptr, &info_ptr);

	/* close the file */
	fclose(fp_png);

//	IGN_dumpjprfile(mapname, minx, miny, maxx, maxy, (maxx-minx)/1000 * TILE_WIDTH, (maxy-miny)/1000*TILE_WIDTH, NULL);
	free(bitmap_memory);
	return true;
}
Exemplo n.º 4
0
bool CreateJNXJpeg(JPEG_tile_list& tiles, long minx, long maxx, long miny, long maxy, CString mapname, long tileWidth, long tileHeight, int nDatabase, int nDrawOrder)
{

	CProgressWindow wndProgress;
	wndProgress.Initialize();

// ToFix as this ain't right!
double dMetersPerPixel = (double) MyMap.GetMetresPerTile()/MyMap.GetPixelsPerTile();
int nTileWidth = MyMap.GetMetresPerTile();

// JKL's code to create the JPEGs

//	long width, height;
//	long x,y;

	unsigned char * bitmap_memory = NULL;

	long bitmap_width, dest_bitmap_width;
	long bitmap_height, dest_bitmap_height;

	long bitmap_memory_size;
//	long square_width;
//	long square_height;
	unsigned char bitmap_palette[1024];
	long bitmap_palette_length;

	if (minx > maxx) {
		long t = minx;
		minx = maxx;
		maxx = t;
	}
	if (miny > maxy) {
		long t = miny;
		miny = maxy;
		maxy = t;
	}

//	long collar = CalculateCollar(minx*1000, maxx*1000, miny*1000, maxy*1000);
	long collar = CalculateCollar(minx, maxx, miny, maxy);
	minx -= (collar/1000)*1000;
	maxx += (collar/1000)*1000;
	miny -= (collar/1000)*1000;
	maxy += (collar/1000)*1000;

	wndProgress.ResetProgressBar("Tile:", ((maxy-miny)*(maxx-minx))/1000*1000);

	bool successful = false;

	if (nDatabase == DBASE_LOCAL) {

		if (MyMap.GetCountry() == COUNTRY_FR && MyMap.GetProduct() == PRODUCT_02) {

			successful = IGN_ReadTiles(minx, maxx, miny, maxy, wndProgress, 
										bitmap_palette, bitmap_palette_length, 
										bitmap_memory , bitmap_memory_size, 
										bitmap_width, dest_bitmap_width,
										bitmap_height, dest_bitmap_height);
		} else {
			successful = OSPro_ReadTiles(minx, maxx, miny, maxy, wndProgress, 
										bitmap_palette, bitmap_palette_length, 
										bitmap_memory , bitmap_memory_size, 
										bitmap_width, dest_bitmap_width,
										bitmap_height, dest_bitmap_height);
		}

	} else {

		bool b_use_TL3 = nDatabase == DBASE_TRACKLOGS_3;

		successful = ReadTracklogsTile(minx, maxx, miny, maxy, wndProgress, 
										bitmap_palette, bitmap_palette_length, 
										bitmap_memory , bitmap_memory_size, 
										bitmap_width, dest_bitmap_width,
										bitmap_height, dest_bitmap_height,
										b_use_TL3);

	}

//	dest_bitmap_width = 400*(maxx-minx-2*collar/1000);
//	dest_bitmap_height = 400*(maxy-miny-2*collar/1000);

	dest_bitmap_width = nTileWidth*((maxx-minx-(2*collar))/1000);
	dest_bitmap_height = nTileWidth*((maxy-miny-(2*collar))/1000);

	if (!successful) {
		if (bitmap_memory)
			free(bitmap_memory);
		return false;
	}

	if (bitmap_memory == NULL) {
		printf("No images to process\n");
		return false;
	}

	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	CLSID	encoderClsid;
	// Get the CLSID of the JPEG encoder.
	GetEncoderClsid(L"image/jpeg", &encoderClsid);

	Gdiplus::EncoderParameters encoderParameters;
	encoderParameters.Count = 1;
	ULONG quality = 80;
	encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
	encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
	encoderParameters.Parameter[0].NumberOfValues = 1;
	encoderParameters.Parameter[0].Value = &quality;

	// xy
	// 00 10 20 30 40
	// 01 11 21 31 41
	// 02 12 22 32 42
	// 03 13 23 33 43
/*
	minx *= 1000;
	maxx *= 1000;
	miny *= 1000;
	maxy *= 1000;
*/
	double lat,lon;
	OZIGEN_NorthingEasting_to_LatLon(maxy-collar, minx+collar, &lat, &lon);
	double minlat = lat;
	double maxlat = lat;
	double minlon = lon;
	double maxlon = lon;
	OZIGEN_NorthingEasting_to_LatLon(maxy-collar, maxx-collar, &lat, &lon);
	if (lat < minlat) minlat = lat;
	if (lat > maxlat) maxlat = lat;
	if (lon < minlon) minlon = lon;
	if (lon > maxlon) maxlon = lon;
	OZIGEN_NorthingEasting_to_LatLon(miny+collar, minx+collar, &lat, &lon);
	if (lat < minlat) minlat = lat;
	if (lat > maxlat) maxlat = lat;
	if (lon < minlon) minlon = lon;
	if (lon > maxlon) maxlon = lon;
	OZIGEN_NorthingEasting_to_LatLon(miny+collar, maxx-collar, &lat, &lon);
	if (lat < minlat) minlat = lat;
	if (lat > maxlat) maxlat = lat;
	if (lon < minlon) minlon = lon;
	if (lon > maxlon) maxlon = lon;

	OZIGEN_LatLon_to_NorthingEasting(minlat, minlon, &lat, &lon);
	OZIGEN_LatLon_to_NorthingEasting(minlat, maxlon, &lat, &lon);
	OZIGEN_LatLon_to_NorthingEasting(maxlat, maxlon, &lat, &lon);
	OZIGEN_LatLon_to_NorthingEasting(maxlat, minlon, &lat, &lon);


	char jnxName[512];
	GetCurrentDirectory(sizeof(jnxName), jnxName);
	CString Currdir = jnxName;						// Save current directory

	// change directory to %TEMP% and make folder 'files'
	GetTempPath(sizeof(jnxName), jnxName);
	SetCurrentDirectory(jnxName);
	CreateDirectory("files", NULL);

//	sprintf(jnxName, "%s\\%s.kmz", Currdir, mapname);

	long nTilesAcross=(dest_bitmap_width+(tileWidth-1))/tileWidth;
	long nTilesDown=(dest_bitmap_height+(tileHeight-1))/tileHeight;

//	KMZ_tiles tiles; - gone global
	
	long across, down;
	for (across=0; across<nTilesAcross; across++) {
		for (down=0; down<nTilesDown; down++) {

			JPEG_tile * pTile = new JPEG_tile;
			pTile->fname.Format("files/c%02d%02d.jpg", down, across);
			pTile->lat_north = minlat+(maxlat-minlat)*(nTilesDown-down)/nTilesDown;
			pTile->lon_east  = minlon+(maxlon-minlon)*(across+1)/nTilesAcross;
			pTile->lat_south = minlat+(maxlat-minlat)*(nTilesDown-down-1)/nTilesDown;
			pTile->lon_west = minlon+(maxlon-minlon)*across/nTilesAcross;
			pTile->offset_x0 = dest_bitmap_width*across/nTilesAcross;
			pTile->offset_x1 = dest_bitmap_width*(across+1)/nTilesAcross;
			pTile->offset_y0 = dest_bitmap_height*down/nTilesDown;
			pTile->offset_y1 = dest_bitmap_height*(down+1)/nTilesDown;
			tiles.AddTail(pTile);
		}
	}

	Global_AddToResultsWindow("Number of tiles = %d",tiles.GetCount());


// SNL
	wndProgress.ResetProgressBar("JPEG:",tiles.GetCount());

	long tileCount=0;
	long index = 1;
	POSITION pos;
	for (pos=tiles.GetHeadPosition(); pos != NULL; tiles.GetNext(pos)) {
		
		wndProgress.ProgressBar();

		if (wndProgress.m_Cancelled) return false;

		JPEG_tile * pTile = (JPEG_tile *)tiles.GetAt(pos);

		Gdiplus::Rect r(0, 0, pTile->offset_x1-pTile->offset_x0, pTile->offset_y1-pTile->offset_y0);
		Gdiplus::Bitmap bmp(r.GetRight(), r.GetBottom(), PixelFormat24bppRGB );
		Gdiplus::BitmapData bmpData;
		bmp.LockBits(&r, Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &bmpData);

		long x,y;
		for (y=pTile->offset_y0; y<pTile->offset_y1; y++) {
			lat = maxlat-(maxlat-minlat)/dest_bitmap_height*y;
			unsigned char * dest_bitmap_offset = ((unsigned char *)bmpData.Scan0) + bmpData.Stride*(y-pTile->offset_y0);
			for (x=pTile->offset_x0; x<pTile->offset_x1; x++) {
				lon = minlon+(maxlon-minlon)/dest_bitmap_width*x;
				double northing, easting;
				OZIGEN_LatLon_to_NorthingEasting(lat, lon, &northing, &easting);
				northing += 0.5;
				easting += 0.5;
				if (northing < miny || easting < minx || northing > maxy || easting > maxx) {
					// No bitmap data present -- white
					*dest_bitmap_offset++ = 255;
					*dest_bitmap_offset++ = 255;
					*dest_bitmap_offset++ = 255;
				} else {
					// Look up colour of bitmap pixel
					unsigned char * pal = bitmap_palette + 
												4*bitmap_memory[(long)((maxy-northing)/dMetersPerPixel)*bitmap_width +
																(long)((easting-minx)/dMetersPerPixel)];
					*dest_bitmap_offset++ = pal[0];
					*dest_bitmap_offset++ = pal[1];
					*dest_bitmap_offset++ = pal[2];
				}
			}
		}
		bmp.UnlockBits(&bmpData);
		wchar_t wbuffer[64];
		int i;
		for (i=0; i<pTile->fname.GetLength(); i++)
			wbuffer[i] = pTile->fname.GetAt(i);
		wbuffer[i] = 0;
		bmp.Save(wbuffer, &encoderClsid, &encoderParameters);
		tileCount++;
	}


	SetCurrentDirectory(Currdir);
//	tiles.RemoveAll();
/*
	if (nPoints) {
		free(points);
		nPoints = 0;
		points = NULL;
	}
*/

	Gdiplus::GdiplusShutdown(gdiplusToken);

	free(bitmap_memory);


// Next bit from Map2JNX

	// up to five levels. nLevels gives the actual count
	static level_t levels[5];

	// information about all files
	static std::list<file_t> files;

	// the JNX file header to be copied to the outfile
	static jnx_hdr_t jnx_hdr;

	// the tile information table for all 5 levels
	static jnx_tile_t tileTable[JNX_MAX_TILES * 5];

	const uint8_t dummy = 0;
	uint32_t tileTableStart = 0;
	uint32_t tileCnt    = 0;

	const char *jnx_copyright = "Unknown";
	const char *jnx_subscname = "BirdsEye";
	const char *jnx_mapname   = "Unknown";

	char *copyright_buf = NULL;
	char *subscname_buf = NULL;
	char *mapname_buf   = NULL;

	double right    = -180.0;
	double top      =  -90.0;
	double left     =  180.0;
	double bottom   =   90.0;

	double scale = 0.0;

	// Number of used levels
	static int32_t nLevels = 1;

    FILE * fid = fopen(mapname+".jnx","wb");
	if(fid == 0)
    {
		// Failed to create file.
        return false;
    }

// New code writer start
//	POSITION pos;
	for (pos=tiles.GetHeadPosition(); pos != NULL; tiles.GetNext(pos)) {
		JPEG_tile * pTile = (JPEG_tile *)tiles.GetAt(pos);
		if (left > pTile->lon_west)
			left = pTile->lon_west;
		if (right < pTile->lon_east)
			right = pTile->lon_east;
		if (top < pTile->lat_north)
			top = pTile->lat_north;
		if (bottom > pTile->lat_south)
			bottom = pTile->lat_south;
	}
	nLevels = 1;
// New code writer end

	jnx_hdr.zorder  = nDrawOrder;
	jnx_hdr.left    = (int32_t)((left   * 0x7FFFFFFF) / 180);
	jnx_hdr.top     = (int32_t)((top    * 0x7FFFFFFF) / 180);
	jnx_hdr.right   = (int32_t)((right  * 0x7FFFFFFF) / 180);
	jnx_hdr.bottom  = (int32_t)((bottom * 0x7FFFFFFF) / 180);

	jnx_hdr.details = nLevels;

	for(int i=0; i<HEADER_BLOCK_SIZE; i++)
	{
		fwrite(&dummy, sizeof(dummy), 1, fid);
	}
	_fseeki64(fid,0,SEEK_SET);
	fwrite(&jnx_hdr, sizeof(jnx_hdr), 1, fid);

	// get all information to write the table of detail levels and the dummy tile table

	int i=0;

	level_t& level  = levels[i];

	level.nTiles   = tiles.GetCount();
	level.offset   = HEADER_BLOCK_SIZE;		// still has to be offset by complete header
	level.scale    = 1509;

	fwrite(&level.nTiles, sizeof(level.nTiles), 1, fid);
	fwrite(&level.offset, sizeof(level.offset), 1, fid);
	fwrite(&level.scale, sizeof(level.scale), 1, fid);
	fwrite(&level.dummy, sizeof(level.dummy), 1, fid);
	fwrite(jnx_copyright, strlen(jnx_copyright) + 1, 1, fid);

	// printf("\n    Level %i: % 5i tiles, offset %08X, scale: %i, %ix%i", i, level.nTiles, level.offset, level.scale, level.tileSize, level.tileSize);

	// write map loader info block
	uint32_t blockVersion = 0x00000009;
	char GUID[40];
	createGUID(GUID);

	fwrite(&blockVersion, sizeof(blockVersion), 1, fid);
	fwrite(GUID, 37, 1, fid);
	fwrite(jnx_subscname, strlen(jnx_subscname) + 1, 1, fid);
	fwrite(&dummy, sizeof(dummy), 1, fid);
	fwrite(&dummy, sizeof(dummy), 1, fid);
	fwrite(&dummy, sizeof(dummy), 1, fid);
	fwrite(jnx_mapname, strlen(jnx_mapname) + 1, 1, fid);
	fwrite(&nLevels , sizeof(nLevels), 1, fid);

	for(int i = 1; i <= nLevels; i++)
	{
		char str[40];
		sprintf(str,"Level %i", i);
		fwrite(str, strlen(str) + 1, 1, fid);
		fwrite(str, strlen(str) + 1, 1, fid);
		fwrite(jnx_copyright, strlen(jnx_copyright) + 1, 1, fid);
		fwrite(&i,sizeof(i), 1, fid);
	}

    // write dummy tile table
    tileTableStart = HEADER_BLOCK_SIZE;
    _fseeki64(fid, tileTableStart, SEEK_SET);
    fwrite(tileTable, sizeof(jnx_tile_t), tileCount, fid);

// New code writer start
	char TempPath[512];
	GetTempPath(sizeof(TempPath), TempPath);
	CString tPath = TempPath;
	tPath = tPath + "\\";

	wndProgress.ResetProgressBar("JNX:",tiles.GetCount());

	for (pos=tiles.GetHeadPosition(); pos != NULL; tiles.GetNext(pos)) {

		JPEG_tile * pTile = (JPEG_tile *)tiles.GetAt(pos);
//		printf("JPEG %d of %d\r", tileCnt, tiles.GetCount());

       jnx_tile_t& tile = tileTable[tileCnt++];

		tile.left    = (int32_t)(pTile->lon_west * 0x7FFFFFFF / 180);
		tile.top     = (int32_t)(pTile->lat_north * 0x7FFFFFFF / 180);
		tile.right   = (int32_t)(pTile->lon_east * 0x7FFFFFFF / 180);
		tile.bottom  = (int32_t)(pTile->lat_south * 0x7FFFFFFF / 180);

		tile.width  = (uint16_t)(pTile->offset_x1-pTile->offset_x0);
		tile.height = (uint16_t)(pTile->offset_y1-pTile->offset_y0);
		tile.offset = (uint32_t)(_ftelli64(fid) & 0x0FFFFFFFF);
		tile.size   = appendJPG(tPath + pTile->fname, fid);
	}

// New code writer end

    // terminate output file
    fwrite("BirdsEye", 8, 1, fid);

    // write final tile table
    _fseeki64(fid, tileTableStart, SEEK_SET);
    fwrite(tileTable, sizeof(jnx_tile_t), tileCount, fid);
 
	// done
    fclose(fid);

exit_CreateJNXJpeg:

    // Clean up
	if (copyright_buf) free(copyright_buf);
	if (subscname_buf) free(subscname_buf);
	if (mapname_buf) free(mapname_buf);

	//POSITION pos;
	for (pos=tiles.GetHeadPosition(); pos != NULL; tiles.GetNext(pos)) {
		JPEG_tile * pTile = (JPEG_tile *)tiles.GetAt(pos);
		DeleteFile(pTile->fname);
	}

	tiles.RemoveAll();

	return true;
}