void progresslog(int x, int y, int z) { char msg[1024]; if(quiet) return; sprintf(msg,"seeding tile %d %d %d",x,y,z); if(lastmsglen) { char erasestring[1024]; int len = MAPCACHE_MIN(1023,lastmsglen); memset(erasestring,' ',len); erasestring[len+1]='\0'; sprintf(erasestring,"\r%%%ds\r",lastmsglen); printf(erasestring," "); } lastmsglen = strlen(msg); printf("%s",msg); fflush(NULL); return; if(queuedtilestot>nthreads) { struct mctimeval now_t; float duration; float totalduration; seededtilestot = queuedtilestot - nthreads; mapcache_gettimeofday(&now_t,NULL); duration = ((now_t.tv_sec-lastlogtime.tv_sec)*1000000+(now_t.tv_usec-lastlogtime.tv_usec))/1000000.0; totalduration = ((now_t.tv_sec-starttime.tv_sec)*1000000+(now_t.tv_usec-starttime.tv_usec))/1000000.0; if(duration>=5) { int Nx, Ny, Ntot, Ncur, ntilessincelast; Nx = (grid_link->grid_limits[z][2]-grid_link->grid_limits[z][0])/tileset->metasize_x; Ny = (grid_link->grid_limits[z][3]-grid_link->grid_limits[z][1])/tileset->metasize_y; Ntot = Nx*Ny; Ncur = (y-grid_link->grid_limits[z][1])/tileset->metasize_y*Nx+(x-grid_link->grid_limits[z][0]+1)/tileset->metasize_x; ntilessincelast = seededtilestot-seededtiles; sprintf(msg,"seeding level %d [%d/%d]: %f metatiles/sec (avg since start: %f)",z,Ncur,Ntot,ntilessincelast/duration, seededtilestot/totalduration); lastlogtime=now_t; seededtiles=seededtilestot; } else { return; } } else { sprintf(msg,"seeding level %d",z); } if(lastmsglen) { char erasestring[1024]; int len = MAPCACHE_MIN(1023,lastmsglen); memset(erasestring,' ',len); erasestring[len+1]='\0'; sprintf(erasestring,"\r%%%ds\r",lastmsglen); printf(erasestring," "); } lastmsglen = strlen(msg); printf("%s",msg); fflush(NULL); }
static void check_tiff_format(mapcache_context *ctx, mapcache_tile *tile, TIFF *hTIFF, const char *filename) { mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)tile->tileset->cache; uint32 imwidth,imheight,tilewidth,tileheight; int16 planarconfig,orientation; uint16 compression; uint16 photometric; int rv; mapcache_grid_level *level; int ntilesx; int ntilesy; TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &imwidth ); TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &imheight ); TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &tilewidth ); TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &tileheight ); /* Test that the TIFF is tiled and not stripped */ if(!TIFFIsTiled(hTIFF)) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not tiled", filename); return; } /* check we have jpeg compression */ rv = TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &compression ); if(rv == 1 && compression != COMPRESSION_JPEG) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not jpeg compressed", filename); return; } /* tiff must be pixel interleaved, not with a single image per band */ rv = TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &planarconfig ); if(rv == 1 && planarconfig != PLANARCONFIG_CONTIG) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not pixel interleaved", filename); return; } /* is this test needed once we now we have JPEG ? */ rv = TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &photometric ); if(rv == 1 && (photometric != PHOTOMETRIC_RGB && photometric != PHOTOMETRIC_YCBCR)) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not RGB: %d", filename); return; } /* the default is top-left, but check just in case */ rv = TIFFGetField( hTIFF, TIFFTAG_ORIENTATION, &orientation ); if(rv == 1 && orientation != ORIENTATION_TOPLEFT) { ctx->set_error(ctx,500,"TIFF file \"%s\" is not top-left oriented", filename); return; } /* check that the tiff internal tiling aligns with the mapcache_grid we are using: * - the tiff tile size must match the grid tile size * - the number of tiles in each direction in the tiff must match what has been * configured for the cache */ level = tile->grid_link->grid->levels[tile->z]; ntilesx = MAPCACHE_MIN(dcache->count_x, level->maxx); ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy); if( tilewidth != tile->grid_link->grid->tile_sx || tileheight != tile->grid_link->grid->tile_sy || imwidth != tile->grid_link->grid->tile_sx * ntilesx || imheight != tile->grid_link->grid->tile_sy * ntilesy ) { ctx->set_error(ctx,500,"TIFF file %s imagesize (%d,%d) and tilesize (%d,%d).\ Expected (%d,%d),(%d,%d)",filename,imwidth,imheight,tilewidth,tileheight, tile->grid_link->grid->tile_sx * ntilesx, tile->grid_link->grid->tile_sy * ntilesy, tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy); return; }
int main(int argc, const char **argv) { /* initialize apr_getopt_t */ apr_getopt_t *opt; const char *configfile=NULL; #ifndef USE_FORK apr_thread_t **threads; apr_threadattr_t *thread_attrs; #endif const char *tileset_name=NULL; const char *tileset_transfer_name=NULL; const char *grid_name = NULL; int *zooms = NULL;//[2]; double *extent = NULL;//[4]; int optch; int rv,n; const char *old = NULL; const char *optarg; apr_table_t *argdimensions; char *dimkey=NULL, *dimvalue=NULL,*key, *last, *optargcpy=NULL; int keyidx; int *metasizes = NULL;//[2]; int metax=-1,metay=-1; #ifdef USE_CLIPPERS const char *ogr_where = NULL; const char *ogr_layer = NULL; const char *ogr_sql = NULL; const char *ogr_datasource = NULL; #endif apr_initialize(); (void) signal(SIGINT,handle_sig_int); apr_pool_create(&ctx.pool,NULL); mapcache_context_init(&ctx); ctx.process_pool = ctx.pool; #ifndef USE_FORK apr_thread_mutex_create((apr_thread_mutex_t**)&ctx.threadlock,APR_THREAD_MUTEX_DEFAULT,ctx.pool); #else ctx.threadlock = NULL; #endif cfg = mapcache_configuration_create(ctx.pool); ctx.config = cfg; ctx.log= mapcache_context_seeding_log; apr_getopt_init(&opt, ctx.pool, argc, argv); seededtiles=seededtilestot=queuedtilestot=0; mapcache_gettimeofday(&starttime,NULL); lastlogtime=starttime; argdimensions = apr_table_make(ctx.pool,3); /* parse the all options based on opt_option[] */ while ((rv = apr_getopt_long(opt, seed_options, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case 'h': return usage(argv[0],NULL); break; case 'f': force = 1; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'c': configfile = optarg; break; case 'g': grid_name = optarg; break; case 't': tileset_name = optarg; break; case 'x': tileset_transfer_name = optarg; break; case 'm': if(!strcmp(optarg,"delete")) { mode = MAPCACHE_CMD_DELETE; } else if(!strcmp(optarg,"transfer")){ mode = MAPCACHE_CMD_TRANSFER; } else if(strcmp(optarg,"seed")){ return usage(argv[0],"invalid mode, expecting \"seed\", \"delete\" or \"transfer\""); } else { mode = MAPCACHE_CMD_SEED; } break; case 'n': nthreads = (int)strtol(optarg, NULL, 10); break; case 'e': if ( MAPCACHE_SUCCESS != mapcache_util_extract_double_list(&ctx, (char*)optarg, ",", &extent, &n) || n != 4 || extent[0] >= extent[2] || extent[1] >= extent[3] ) { return usage(argv[0], "failed to parse extent, expecting comma separated 4 doubles"); } break; case 'z': if ( MAPCACHE_SUCCESS != mapcache_util_extract_int_list(&ctx, (char*)optarg, ",", &zooms, &n) || n != 2 || zooms[0] > zooms[1]) { return usage(argv[0], "failed to parse zooms, expecting comma separated 2 ints"); } else { minzoom = zooms[0]; maxzoom = zooms[1]; } break; case 'M': if ( MAPCACHE_SUCCESS != mapcache_util_extract_int_list(&ctx, (char*)optarg, ",", &metasizes, &n) || n != 2 || metasizes[0] <= 0 || metasizes[1] <=0) { return usage(argv[0], "failed to parse metasize, expecting comma separated 2 positive ints (e.g. -M 8,8"); } else { metax = metasizes[0]; metay = metasizes[1]; } break; case 'o': old = optarg; break; case 'D': optargcpy = apr_pstrdup(ctx.pool,optarg); keyidx = 0; for (key = apr_strtok(optargcpy, "=", &last); key != NULL; key = apr_strtok(NULL, "=", &last)) { if(keyidx == 0) { dimkey = key; } else { dimvalue = key; } keyidx++; } if(keyidx!=2 || !dimkey || !dimvalue || !*dimkey || !*dimvalue) { return usage(argv[0], "failed to parse dimension, expecting DIMNAME=DIMVALUE"); } apr_table_set(argdimensions,dimkey,dimvalue); break; #ifdef USE_CLIPPERS case 'd': ogr_datasource = optarg; break; case 's': ogr_sql = optarg; break; case 'l': ogr_layer = optarg; break; case 'w': ogr_where = optarg; break; #endif } } if (rv != APR_EOF) { return usage(argv[0],"bad options"); } if( ! configfile ) { return usage(argv[0],"config not specified"); } else { mapcache_configuration_parse(&ctx,configfile,cfg,0); if(ctx.get_error(&ctx)) return usage(argv[0],ctx.get_error_message(&ctx)); mapcache_configuration_post_config(&ctx,cfg); if(ctx.get_error(&ctx)) return usage(argv[0],ctx.get_error_message(&ctx)); } #ifdef USE_CLIPPERS if(extent && ogr_datasource) { return usage(argv[0], "cannot specify both extent and ogr-datasource"); } if( ogr_sql && ( ogr_where || ogr_layer )) { return usage(argv[0], "ogr-where or ogr_layer cannot be used in conjunction with ogr-sql"); } if(ogr_datasource) { OGRDataSourceH hDS = NULL; OGRLayerH layer = NULL; OGRRegisterAll(); hDS = OGROpen( ogr_datasource, FALSE, NULL ); if( hDS == NULL ) { printf( "OGR Open failed\n" ); exit( 1 ); } if(ogr_sql) { layer = OGR_DS_ExecuteSQL( hDS, ogr_sql, NULL, NULL); if(!layer) { return usage(argv[0],"aborting"); } } else { int nLayers = OGR_DS_GetLayerCount(hDS); if(nLayers>1 && !ogr_layer) { return usage(argv[0],"ogr datastore contains more than one layer. please specify which one to use with --ogr-layer"); } else { if(ogr_layer) { layer = OGR_DS_GetLayerByName(hDS,ogr_layer); } else { layer = OGR_DS_GetLayer(hDS,0); } if(!layer) { return usage(argv[0],"aborting"); } if(ogr_where) { if(OGRERR_NONE != OGR_L_SetAttributeFilter(layer, ogr_where)) { return usage(argv[0],"aborting"); } } } } if((nClippers=OGR_L_GetFeatureCount(layer, TRUE)) == 0) { return usage(argv[0],"no features in provided ogr parameters, cannot continue"); } initGEOS(notice, log_and_exit); clippers = (const GEOSPreparedGeometry**)malloc(nClippers*sizeof(GEOSPreparedGeometry*)); OGRFeatureH hFeature; GEOSWKTReader *geoswktreader = GEOSWKTReader_create(); OGR_L_ResetReading(layer); extent = apr_pcalloc(ctx.pool,4*sizeof(double)); int f=0; while( (hFeature = OGR_L_GetNextFeature(layer)) != NULL ) { OGRGeometryH geom = OGR_F_GetGeometryRef(hFeature); if(!geom || !OGR_G_IsValid(geom)) continue; char *wkt; OGR_G_ExportToWkt(geom,&wkt); GEOSGeometry *geosgeom = GEOSWKTReader_read(geoswktreader,wkt); free(wkt); clippers[f] = GEOSPrepare(geosgeom); //GEOSGeom_destroy(geosgeom); OGREnvelope ogr_extent; OGR_G_GetEnvelope (geom, &ogr_extent); if(f == 0) { extent[0] = ogr_extent.MinX; extent[1] = ogr_extent.MinY; extent[2] = ogr_extent.MaxX; extent[3] = ogr_extent.MaxY; } else { extent[0] = MAPCACHE_MIN(ogr_extent.MinX, extent[0]); extent[1] = MAPCACHE_MIN(ogr_extent.MinY, extent[1]); extent[2] = MAPCACHE_MAX(ogr_extent.MaxX, extent[2]); extent[3] = MAPCACHE_MAX(ogr_extent.MaxY, extent[3]); } OGR_F_Destroy( hFeature ); f++; } nClippers = f; } #endif if( ! tileset_name ) { return usage(argv[0],"tileset not specified"); } else { tileset = mapcache_configuration_get_tileset(cfg,tileset_name); if(!tileset) { return usage(argv[0], "tileset not found in configuration"); } if( ! grid_name ) { grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*); } else {
/** * Compute a palette for the given RGBA rasterBuffer using a median cut quantization. * - rb: the rasterBuffer to quantize * - reqcolors: the desired number of colors the palette should contain. will be set * with the actual number of entries in the computed palette * - palette: preallocated array of palette entries that will be populated by the * function * - maxval: max value of pixel intensity. In some cases, the input data has to * be rescaled to compute the quantization. if the returned value of maxscale is * less than 255, this means that the input pixels have been rescaled, and that * the returned palette must be upscaled before being written to the png file * - forced_palette: entries that should appear in the computed palette * - num_forced_palette_entries: number of entries contained in "force_palette". if 0, * "force_palette" can be NULL */ int _mapcache_imageio_quantize_image(mapcache_image *rb, unsigned int *reqcolors, rgbaPixel *palette, unsigned int *maxval, rgbaPixel *forced_palette, int num_forced_palette_entries) { rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */ register rgbaPixel *pP; register int col; unsigned char newmaxval; acolorhist_vector achv, acolormap=NULL; int row; int colors; int newcolors = 0; int x; /* int channels; */ *maxval = 255; apixels=(rgbaPixel**)malloc(rb->h*sizeof(rgbaPixel**)); if(!apixels) return MAPCACHE_FAILURE; for(row=0;row<rb->h;row++) { apixels[row]=(rgbaPixel*)(&(rb->data[row * rb->stride])); } /* ** Step 2: attempt to make a histogram of the colors, unclustered. ** If at first we don't succeed, lower maxval to increase color ** coherence and try again. This will eventually terminate, with ** maxval at worst 15, since 32^3 is approximately MAXCOLORS. [GRR POSSIBLE BUG: what about 32^4 ?] */ for ( ; ; ) { achv = pam_computeacolorhist( apixels, rb->w, rb->h, MAXCOLORS, &colors ); if ( achv != (acolorhist_vector) 0 ) break; newmaxval = *maxval / 2; for ( row = 0; row < rb->h; ++row ) for ( col = 0, pP = apixels[row]; col < rb->w; ++col, ++pP ) PAM_DEPTH( *pP, *pP, *maxval, newmaxval ); *maxval = newmaxval; } newcolors = MAPCACHE_MIN(colors, *reqcolors); acolormap = mediancut(achv, colors, rb->w*rb->h, *maxval, newcolors); pam_freeacolorhist(achv); *reqcolors = newcolors; for (x = 0; x < newcolors; ++x) { palette[x].r = acolormap[x].acolor.r; palette[x].g = acolormap[x].acolor.g; palette[x].b = acolormap[x].acolor.b; palette[x].a = acolormap[x].acolor.a; } free(acolormap); free(apixels); return MAPCACHE_SUCCESS; }