void idwProcessing(layerObj *layer, processingParams *psz) { const char *pszProcessing = msLayerGetProcessingKey( layer, "IDW_POWER" ); if(pszProcessing) { psz->power = atof(pszProcessing); if(psz->power > 12){ psz->power = 12; } } else{ psz->power = 2.0; } pszProcessing = msLayerGetProcessingKey( layer, "IDW_RADIUS" ); if(pszProcessing){ psz->radius = atof(pszProcessing); } else { psz->radius = MAX(layer->map->width,layer->map->height); } pszProcessing = msLayerGetProcessingKey( layer, "IDW_COMPUTE_BORDERS" ); if(pszProcessing && strcasecmp(pszProcessing,"OFF")){ psz->expand_searchrect = 1; } else { psz->expand_searchrect = 0; } }
/* eventually add a class to the layer to get the diameter from an attribute */ int pieLayerProcessDynamicDiameter(layerObj *layer) { const char *chartRangeProcessingKey=NULL; char *attrib; float mindiameter=-1, maxdiameter, minvalue, maxvalue; classObj *newclass; styleObj *newstyle; const char *chartSizeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE" ); if(chartSizeProcessingKey != NULL) return MS_FALSE; chartRangeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE_RANGE" ); if(chartRangeProcessingKey==NULL) return MS_FALSE; attrib = msSmallMalloc(strlen(chartRangeProcessingKey)+1); switch(sscanf(chartRangeProcessingKey,"%s %f %f %f %f",attrib, &mindiameter,&maxdiameter,&minvalue,&maxvalue)) { case 1: /*we only have the attribute*/ case 5: /*we have the attribute and the four range values*/ break; default: free(attrib); msSetError(MS_MISCERR, "Chart Layer format error for processing key \"CHART_RANGE\"", "msDrawChartLayer()"); return MS_FAILURE; } /*create a new class in the layer containing the wanted attribute * as the SIZE of its first STYLE*/ newclass=msGrowLayerClasses(layer); if(newclass==NULL) { free(attrib); return MS_FAILURE; } initClass(newclass); layer->numclasses++; /*create and attach a new styleObj to our temp class * and bind the wanted attribute to its SIZE */ newstyle=msGrowClassStyles(newclass); if(newstyle==NULL) { free(attrib); return MS_FAILURE; } initStyle(newstyle); newclass->numstyles++; newclass->name=(char*)msStrdup("__MS_SIZE_ATTRIBUTE_"); newstyle->bindings[MS_STYLE_BINDING_SIZE].item=msStrdup(attrib); newstyle->numbindings++; free(attrib); return MS_TRUE; }
void *msConnPoolRequest( layerObj *layer ) { int i; const char* close_connection; if( layer->connection == NULL ) return NULL; /* check if we must always create a new connection */ close_connection = msLayerGetProcessingKey( layer, "CLOSE_CONNECTION" ); if( close_connection && strcasecmp(close_connection,"ALWAYS") == 0 ) return NULL; msAcquireLock( TLOCK_POOL ); for( i = 0; i < connectionCount; i++ ) { connectionObj *conn = connections + i; if( layer->connectiontype == conn->connectiontype && strcasecmp( layer->connection, conn->connection ) == 0 && (conn->ref_count == 0 || conn->thread_id == msGetThreadId()) && conn->lifespan != MS_LIFE_SINGLE) { void *conn_handle = NULL; conn->ref_count++; conn->thread_id = msGetThreadId(); conn->last_used = time(NULL); if( layer->debug ) { msDebug( "msConnPoolRequest(%s,%s) -> got %p\n", layer->name, layer->connection, conn->conn_handle ); conn->debug = layer->debug; } conn_handle = conn->conn_handle; msReleaseLock( TLOCK_POOL ); return conn_handle; } } msReleaseLock( TLOCK_POOL ); return NULL; }
int agg2StartNewLayer(imageObj *img, mapObj*map, layerObj *layer) { AGG2Renderer *r = AGG_RENDERER(img); char *sgamma = msLayerGetProcessingKey( layer, "GAMMA" ); double gamma; if(sgamma) { gamma = atof(sgamma); if(gamma <= 0 || gamma >= 1) gamma = 0.75; } else { gamma = r->default_gamma; } if(r->gamma_function.end() != gamma) { r->gamma_function.end(gamma); r->m_rasterizer_aa_gamma.gamma(r->gamma_function); } return MS_SUCCESS; }
/* * generic function for drawing a legend icon. (added for bug #2348) * renderer specific drawing functions shouldn't be called directly, but through * this function */ int msDrawLegendIcon(mapObj *map, layerObj *lp, classObj *theclass, int width, int height, imageObj *image, int dstX, int dstY) { int i, type, hasmarkersymbol; double offset; shapeObj box, zigzag; pointObj marker; char szPath[MS_MAXPATHLEN]; styleObj outline_style; imageObj *image_draw = image; int originalopacity = lp->opacity; rendererVTableObj *renderer; outputFormatObj *transFormat = NULL, *altFormat=NULL; const char *alternativeFormatString = NULL; if(!MS_RENDERER_PLUGIN(image->format)) { msSetError(MS_MISCERR,"unsupported image format","msDrawLegendIcon()"); return MS_FAILURE; } alternativeFormatString = msLayerGetProcessingKey(lp, "RENDERER"); if (MS_RENDERER_PLUGIN(image_draw->format) && alternativeFormatString!=NULL && (altFormat= msSelectOutputFormat(map, alternativeFormatString))) { msInitializeRendererVTable(altFormat); image_draw = msImageCreate(image->width, image->height, altFormat, image->imagepath, image->imageurl, map->resolution, map->defresolution, &map->imagecolor); renderer = MS_IMAGE_RENDERER(image_draw); } else { renderer = MS_IMAGE_RENDERER(image_draw); if (lp->opacity > 0 && lp->opacity < 100) { if (!renderer->supports_transparent_layers) { image_draw = msImageCreate(image->width, image->height, image->format, image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL); if (!image_draw) { msSetError(MS_MISCERR, "Unable to initialize temporary transparent image.", "msDrawLegendIcon()"); return (MS_FAILURE); } /* set opacity to full, as the renderer should be rendering a fully opaque image */ lp->opacity=100; } } } if(renderer->supports_clipping && MS_VALID_COLOR(map->legend.outlinecolor)) { /* keep GD specific code here for now as it supports clipping */ rectObj clip; clip.maxx = dstX + width - 1; clip.maxy = dstY + height -1; clip.minx = dstX; clip.miny = dstY; renderer->setClip(image_draw,clip); } /* initialize the box used for polygons and for outlines */ box.line = (lineObj *)msSmallMalloc(sizeof(lineObj)); box.numlines = 1; box.line[0].point = (pointObj *)msSmallMalloc(sizeof(pointObj)*5); box.line[0].numpoints = 5; box.line[0].point[0].x = dstX + 0.5; box.line[0].point[0].y = dstY + 0.5; box.line[0].point[1].x = dstX + width - 0.5; box.line[0].point[1].y = dstY + 0.5; box.line[0].point[2].x = dstX + width - 0.5; box.line[0].point[2].y = dstY + height - 0.5; box.line[0].point[3].x = dstX + 0.5; box.line[0].point[3].y = dstY + height - 0.5; box.line[0].point[4].x = box.line[0].point[0].x; box.line[0].point[4].y = box.line[0].point[0].y; box.line[0].numpoints = 5; /* if the class has a keyimage, treat it as a point layer * (the keyimage will be treated there) */ if(theclass->keyimage != NULL) { type = MS_LAYER_POINT; } else { /* some polygon layers may be better drawn using zigzag if there is no fill */ type = lp->type; if(type == MS_LAYER_POLYGON) { type = MS_LAYER_LINE; for(i=0; i<theclass->numstyles; i++) { if(MS_VALID_COLOR(theclass->styles[i]->color)) { /* there is a fill */ type = MS_LAYER_POLYGON; break; } } } } /* ** now draw the appropriate color/symbol/size combination */ switch(type) { case MS_LAYER_ANNOTATION: marker.x = dstX + MS_NINT(width / 2.0); marker.y = dstY + MS_NINT(height / 2.0); hasmarkersymbol = 0; for(i=0; i<theclass->numstyles; i++) { if (theclass->styles[i]->symbol < map->symbolset.numsymbols && theclass->styles[i]->symbol > 0) { hasmarkersymbol = 1; break; } } if (hasmarkersymbol) { for(i=0; i<theclass->numstyles; i++) msDrawMarkerSymbol(&map->symbolset, image_draw, &marker, theclass->styles[i], lp->scalefactor); } else if (theclass->labels && theclass->numlabels > 0) { labelObj *label = theclass->labels[0]; /* use the first label definition */ double lsize = label->size; double langle = label->angle; int lpos = label->position; int loffsetx = label->offsetx; int loffsety = label->offsety; label->offsetx = 0; label->offsety = 0; label->angle = 0; label->position = MS_CC; if (label->type == MS_TRUETYPE) label->size = height; msDrawLabel(map, image_draw, marker, (char*)"Az", label,1.0); label->size = lsize; label->position = lpos; label->angle = langle; label->offsetx = loffsetx; label->offsety = loffsety; } break; case MS_LAYER_POINT: marker.x = dstX + MS_NINT(width / 2.0); marker.y = dstY + MS_NINT(height / 2.0); if(theclass->keyimage != NULL) { int symbolNum; styleObj imgStyle; symbolObj *symbol=NULL; symbolNum = msAddImageSymbol(&(map->symbolset), msBuildPath(szPath, map->mappath, theclass->keyimage)); if(symbolNum == -1) { msSetError(MS_GDERR, "Failed to open legend key image", "msCreateLegendIcon()"); return(MS_FAILURE); } symbol = map->symbolset.symbol[symbolNum]; initStyle(&imgStyle); /*set size so that symbol will be scaled properly #3296*/ if (width/symbol->sizex < height/symbol->sizey) imgStyle.size = symbol->sizey*(width/symbol->sizex); else imgStyle.size = symbol->sizey*(height/symbol->sizey); if (imgStyle.size > imgStyle.maxsize) imgStyle.maxsize = imgStyle.size; imgStyle.symbol = symbolNum; msDrawMarkerSymbol(&map->symbolset,image_draw,&marker,&imgStyle,lp->scalefactor); /* TO DO: we may want to handle this differently depending on the relative size of the keyimage */ } else { for(i=0; i<theclass->numstyles; i++) msDrawMarkerSymbol(&map->symbolset, image_draw, &marker, theclass->styles[i], lp->scalefactor); } break; case MS_LAYER_LINE: offset = 1; /* To set the offset, we only check the size/width parameter of the first style */ if (theclass->numstyles > 0) { if (theclass->styles[0]->symbol > 0 && theclass->styles[0]->symbol < map->symbolset.numsymbols && map->symbolset.symbol[theclass->styles[0]->symbol]->type != MS_SYMBOL_SIMPLE) offset = theclass->styles[0]->size/2; else offset = theclass->styles[0]->width/2; } zigzag.line = (lineObj *)msSmallMalloc(sizeof(lineObj)); zigzag.numlines = 1; zigzag.line[0].point = (pointObj *)msSmallMalloc(sizeof(pointObj)*4); zigzag.line[0].numpoints = 4; zigzag.line[0].point[0].x = dstX + offset; zigzag.line[0].point[0].y = dstY + height - offset; zigzag.line[0].point[1].x = dstX + MS_NINT(width / 3.0) - 1; zigzag.line[0].point[1].y = dstY + offset; zigzag.line[0].point[2].x = dstX + MS_NINT(2.0 * width / 3.0) - 1; zigzag.line[0].point[2].y = dstY + height - offset; zigzag.line[0].point[3].x = dstX + width - offset; zigzag.line[0].point[3].y = dstY + offset; for(i=0; i<theclass->numstyles; i++) msDrawLineSymbol(&map->symbolset, image_draw, &zigzag, theclass->styles[i], lp->scalefactor); free(zigzag.line[0].point); free(zigzag.line); break; case MS_LAYER_CIRCLE: case MS_LAYER_RASTER: case MS_LAYER_CHART: case MS_LAYER_POLYGON: for(i=0; i<theclass->numstyles; i++) msDrawShadeSymbol(&map->symbolset, image_draw, &box, theclass->styles[i], lp->scalefactor); break; default: return MS_FAILURE; break; } /* end symbol drawing */ /* handle an outline if necessary */ if(MS_VALID_COLOR(map->legend.outlinecolor)) { initStyle(&outline_style); outline_style.color = map->legend.outlinecolor; msDrawLineSymbol(&map->symbolset, image_draw, &box, &outline_style, 1.0); /* reset clipping rectangle */ if(renderer->supports_clipping) renderer->resetClip(image_draw); } if (altFormat) { rendererVTableObj *renderer = MS_IMAGE_RENDERER(image); rendererVTableObj *altrenderer = MS_IMAGE_RENDERER(image_draw); rasterBufferObj rb; memset(&rb,0,sizeof(rasterBufferObj)); altrenderer->getRasterBufferHandle(image_draw,&rb); renderer->mergeRasterBuffer(image,&rb,lp->opacity*0.01,0,0,0,0,rb.width,rb.height); /* * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain * symbols that reference it. We want to remove those references before the altFormat is destroyed * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking * it's for him. */ for(i=0; i<map->symbolset.numsymbols; i++) { if (map->symbolset.symbol[i]!=NULL) { symbolObj *s = map->symbolset.symbol[i]; if(s->renderer == altrenderer) { altrenderer->freeSymbol(s); s->renderer = NULL; } } } msFreeImage(image_draw); } else if(image != image_draw) { rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw); rasterBufferObj rb; memset(&rb,0,sizeof(rasterBufferObj)); lp->opacity = originalopacity; renderer->getRasterBufferHandle(image_draw,&rb); renderer->mergeRasterBuffer(image,&rb,lp->opacity*0.01,0,0,0,0,rb.width,rb.height); msFreeImage(image_draw); /* deref and possibly free temporary transparent output format. */ msApplyOutputFormat( &transFormat, NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE ); } free(box.line[0].point); free(box.line); return MS_SUCCESS; }
/* * generic function for drawing a legend icon. (added for bug #2348) * renderer specific drawing functions shouldn't be called directly, but through * this function */ int msDrawLegendIcon(mapObj *map, layerObj *lp, classObj *theclass, int width, int height, imageObj *image, int dstX, int dstY, int scale_independant, class_hittest *hittest) { int i, type, hasmarkersymbol, ret=MS_SUCCESS; double offset; double polygon_contraction = 0.5; /* used to account for the width of a polygon's outline */ shapeObj box, zigzag; lineObj box_line,zigzag_line; pointObj box_point[5], zigzag_point[4]; pointObj marker; char szPath[MS_MAXPATHLEN]; styleObj outline_style; imageObj *image_draw = image; int originalopacity = lp->opacity; rendererVTableObj *renderer; outputFormatObj *transFormat = NULL, *altFormat=NULL; const char *alternativeFormatString = NULL; if(!MS_RENDERER_PLUGIN(image->format)) { msSetError(MS_MISCERR,"unsupported image format","msDrawLegendIcon()"); return MS_FAILURE; } alternativeFormatString = msLayerGetProcessingKey(lp, "RENDERER"); if (MS_RENDERER_PLUGIN(image_draw->format) && alternativeFormatString!=NULL && (altFormat= msSelectOutputFormat(map, alternativeFormatString))) { msInitializeRendererVTable(altFormat); image_draw = msImageCreate(image->width, image->height, altFormat, image->imagepath, image->imageurl, map->resolution, map->defresolution, &map->imagecolor); renderer = MS_IMAGE_RENDERER(image_draw); } else { renderer = MS_IMAGE_RENDERER(image_draw); if (lp->opacity > 0 && lp->opacity < 100) { if (!renderer->supports_transparent_layers) { image_draw = msImageCreate(image->width, image->height, image->format, image->imagepath, image->imageurl, map->resolution, map->defresolution, NULL); if (!image_draw) { msSetError(MS_MISCERR, "Unable to initialize temporary transparent image.", "msDrawLegendIcon()"); return (MS_FAILURE); } /* set opacity to full, as the renderer should be rendering a fully opaque image */ lp->opacity=100; } } } if(renderer->supports_clipping && MS_VALID_COLOR(map->legend.outlinecolor)) { /* keep GD specific code here for now as it supports clipping */ rectObj clip; clip.maxx = dstX + width - 1; clip.maxy = dstY + height -1; clip.minx = dstX; clip.miny = dstY; renderer->setClip(image_draw,clip); } /* if the class has a keyimage, treat it as a point layer * (the keyimage will be treated there) */ if(theclass->keyimage != NULL) { type = MS_LAYER_POINT; } else { /* some polygon layers may be better drawn using zigzag if there is no fill */ type = lp->type; if(type == MS_LAYER_POLYGON) { type = MS_LAYER_LINE; for(i=0; i<theclass->numstyles; i++) { if(MS_VALID_COLOR(theclass->styles[i]->color)) { /* there is a fill */ type = MS_LAYER_POLYGON; } if(MS_VALID_COLOR(theclass->styles[i]->outlinecolor)) { /* there is an outline */ polygon_contraction = MS_MAX(polygon_contraction, theclass->styles[i]->width / 2.0); } } } } /* initialize the box used for polygons and for outlines */ box.line = &box_line; box.numlines = 1; box.line[0].point = box_point; box.line[0].numpoints = 5; box.line[0].point[0].x = dstX + polygon_contraction; box.line[0].point[0].y = dstY + polygon_contraction; box.line[0].point[1].x = dstX + width - polygon_contraction; box.line[0].point[1].y = dstY + polygon_contraction; box.line[0].point[2].x = dstX + width - polygon_contraction; box.line[0].point[2].y = dstY + height - polygon_contraction; box.line[0].point[3].x = dstX + polygon_contraction; box.line[0].point[3].y = dstY + height - polygon_contraction; box.line[0].point[4].x = box.line[0].point[0].x; box.line[0].point[4].y = box.line[0].point[0].y; /* ** now draw the appropriate color/symbol/size combination */ switch(type) { case MS_LAYER_POINT: marker.x = dstX + MS_NINT(width / 2.0); marker.y = dstY + MS_NINT(height / 2.0); if(theclass->keyimage != NULL) { int symbolNum; styleObj imgStyle; symbolObj *symbol=NULL; for(symbolNum=0; symbolNum<theclass->numstyles; symbolNum++) symbolNum = msAddImageSymbol(&(map->symbolset), msBuildPath(szPath, map->mappath, theclass->keyimage)); if(symbolNum == -1) { msSetError(MS_IMGERR, "Failed to open legend key image", "msCreateLegendIcon()"); return(MS_FAILURE); } symbol = map->symbolset.symbol[symbolNum]; initStyle(&imgStyle); /*set size so that symbol will be scaled properly #3296*/ if (width/symbol->sizex < height/symbol->sizey) imgStyle.size = symbol->sizey*(width/symbol->sizex); else imgStyle.size = symbol->sizey*(height/symbol->sizey); if (imgStyle.size > imgStyle.maxsize) imgStyle.maxsize = imgStyle.size; imgStyle.symbol = symbolNum; ret = msDrawMarkerSymbol(map ,image_draw,&marker,&imgStyle,lp->scalefactor * image_draw->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; /* TO DO: we may want to handle this differently depending on the relative size of the keyimage */ } else { for(i=0; i<theclass->numstyles; i++) { if(!scale_independant && map->scaledenom > 0) { styleObj *lp = theclass->styles[i]; if((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom)) continue; if((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom)) continue; } if(hittest && hittest->stylehits[i].status == 0) continue; ret = msDrawMarkerSymbol(map, image_draw, &marker, theclass->styles[i], lp->scalefactor * image->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } } break; case MS_LAYER_LINE: offset = 1; /* To set the offset, we only check the size/width parameter of the first style */ if (theclass->numstyles > 0) { if (theclass->styles[0]->symbol > 0 && theclass->styles[0]->symbol < map->symbolset.numsymbols && map->symbolset.symbol[theclass->styles[0]->symbol]->type != MS_SYMBOL_SIMPLE) offset = theclass->styles[0]->size/2; else offset = theclass->styles[0]->width/2; } zigzag.line = &zigzag_line; zigzag.numlines = 1; zigzag.line[0].point = zigzag_point; zigzag.line[0].numpoints = 4; zigzag.line[0].point[0].x = dstX + offset; zigzag.line[0].point[0].y = dstY + height - offset; zigzag.line[0].point[1].x = dstX + MS_NINT(width / 3.0) - 1; zigzag.line[0].point[1].y = dstY + offset; zigzag.line[0].point[2].x = dstX + MS_NINT(2.0 * width / 3.0) - 1; zigzag.line[0].point[2].y = dstY + height - offset; zigzag.line[0].point[3].x = dstX + width - offset; zigzag.line[0].point[3].y = dstY + offset; for(i=0; i<theclass->numstyles; i++) { if(!scale_independant && map->scaledenom > 0) { styleObj *lp = theclass->styles[i]; if((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom)) continue; if((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom)) continue; } if(hittest && hittest->stylehits[i].status == 0) continue; if (theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE || theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT || theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) { ret = msDrawLineSymbol(map, image_draw, &zigzag, theclass->styles[i], lp->scalefactor * image_draw->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } else { ret = msDrawTransformedShape(map, image_draw, &zigzag, theclass->styles[i], lp->scalefactor * image_draw->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } } break; case MS_LAYER_CIRCLE: case MS_LAYER_RASTER: case MS_LAYER_CHART: case MS_LAYER_POLYGON: for(i=0; i<theclass->numstyles; i++) { if(!scale_independant && map->scaledenom > 0) { styleObj *lp = theclass->styles[i]; if((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom)) continue; if((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom)) continue; } if(hittest && hittest->stylehits[i].status == 0) continue; if (theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE || theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT || theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOLY) { ret = msDrawShadeSymbol(map, image_draw, &box, theclass->styles[i], lp->scalefactor * image_draw->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } else { ret = msDrawTransformedShape(map, image_draw, &box, theclass->styles[i], lp->scalefactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } } break; default: return MS_FAILURE; break; } /* end symbol drawing */ /* handle label styles */ for(i=0; i<theclass->numlabels; i++) { labelObj *l = theclass->labels[i]; if(!scale_independant && map->scaledenom > 0) { if(msScaleInBounds(map->scaledenom, l->minscaledenom, l->maxscaledenom)) { int j; for(j=0; j<l->numstyles; j++) { styleObj *s = l->styles[j]; marker.x = dstX + MS_NINT(width / 2.0); marker.y = dstY + MS_NINT(height / 2.0); if(s->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) { ret = msDrawMarkerSymbol(map, image_draw, &marker, s, lp->scalefactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } } } } } /* handle "pure" text layers, i.e. layers with no symbology */ hasmarkersymbol = 0; if(theclass->numstyles == 0) { for(i=0; i<theclass->numlabels; i++) { labelObj *l = theclass->labels[i]; if(!scale_independant && map->scaledenom > 0) { if(msScaleInBounds(map->scaledenom, l->minscaledenom, l->maxscaledenom)) { int j; for(j=0; j<l->numstyles; j++) { styleObj *s = l->styles[j]; if(s->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) { hasmarkersymbol = 1; } } } } } } else { hasmarkersymbol = 1; } if(!hasmarkersymbol && theclass->numlabels>0) { textSymbolObj ts; pointObj textstartpt; marker.x = dstX + MS_NINT(width / 2.0); marker.y = dstY + MS_NINT(height / 2.0); initTextSymbol(&ts); msPopulateTextSymbolForLabelAndString(&ts,theclass->labels[0],msStrdup("Az"),lp->scalefactor*image_draw->resolutionfactor,image_draw->resolutionfactor, duplicate_always); ts.label->size = height - 1; ret = msComputeTextPath(map,&ts); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; textstartpt = get_metrics(&marker,MS_CC,ts.textpath,0,0,0,0,NULL); ret = msDrawTextSymbol(map,image_draw, textstartpt, &ts); freeTextSymbol(&ts); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; } /* handle an outline if necessary */ if(MS_VALID_COLOR(map->legend.outlinecolor)) { initStyle(&outline_style); outline_style.color = map->legend.outlinecolor; ret = msDrawLineSymbol(map, image_draw, &box, &outline_style, image_draw->resolutionfactor); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; /* reset clipping rectangle */ if(renderer->supports_clipping) renderer->resetClip(image_draw); } if (altFormat) { rendererVTableObj *renderer = MS_IMAGE_RENDERER(image); rendererVTableObj *altrenderer = MS_IMAGE_RENDERER(image_draw); rasterBufferObj rb; memset(&rb,0,sizeof(rasterBufferObj)); ret = altrenderer->getRasterBufferHandle(image_draw,&rb); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; ret = renderer->mergeRasterBuffer(image,&rb,lp->opacity*0.01,0,0,0,0,rb.width,rb.height); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; /* * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain * symbols that reference it. We want to remove those references before the altFormat is destroyed * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking * it's for him. */ for(i=0; i<map->symbolset.numsymbols; i++) { if (map->symbolset.symbol[i]!=NULL) { symbolObj *s = map->symbolset.symbol[i]; if(s->renderer == altrenderer) { altrenderer->freeSymbol(s); s->renderer = NULL; } } } } else if(image != image_draw) { rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw); rasterBufferObj rb; memset(&rb,0,sizeof(rasterBufferObj)); lp->opacity = originalopacity; ret = renderer->getRasterBufferHandle(image_draw,&rb); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; ret = renderer->mergeRasterBuffer(image,&rb,lp->opacity*0.01,0,0,0,0,rb.width,rb.height); if(UNLIKELY(ret == MS_FAILURE)) goto legend_icon_cleanup; /* deref and possibly free temporary transparent output format. */ msApplyOutputFormat( &transFormat, NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE ); } legend_icon_cleanup: if(image != image_draw) { msFreeImage(image_draw); } return ret; }
void msConnPoolRegister( layerObj *layer, void *conn_handle, void (*close_func)( void * ) ) { const char *close_connection = NULL; connectionObj *conn = NULL; if( layer->debug ) msDebug( "msConnPoolRegister(%s,%s,%p)\n", layer->name, layer->connection, conn_handle ); /* -------------------------------------------------------------------- */ /* We can't meaningful keep a connection with no connection or */ /* connection type string on the layer. */ /* -------------------------------------------------------------------- */ if( layer->connection == NULL ) { if( layer->tileindex != NULL && layer->connectiontype == MS_OGR ) { /* this is ok, no need to make a fuss */ } else { msDebug( "%s: Missing CONNECTION on layer %s.\n", "msConnPoolRegister()", layer->name ); msSetError( MS_MISCERR, "Missing CONNECTION on layer %s.", "msConnPoolRegister()", layer->name ); } return; } /* -------------------------------------------------------------------- */ /* Grow the array of connection information objects if needed. */ /* -------------------------------------------------------------------- */ msAcquireLock( TLOCK_POOL ); if( connectionCount == connectionMax ) { connectionMax += 10; connections = (connectionObj *) realloc(connections, sizeof(connectionObj) * connectionMax ); if( connections == NULL ) { msSetError(MS_MEMERR, NULL, "msConnPoolRegister()"); msReleaseLock( TLOCK_POOL ); return; } } /* -------------------------------------------------------------------- */ /* Set the new connection information. */ /* -------------------------------------------------------------------- */ conn = connections + connectionCount; connectionCount++; conn->connectiontype = layer->connectiontype; conn->connection = msStrdup( layer->connection ); conn->close = close_func; conn->ref_count = 1; conn->thread_id = msGetThreadId(); conn->last_used = time(NULL); conn->conn_handle = conn_handle; conn->debug = layer->debug; /* -------------------------------------------------------------------- */ /* Categorize the connection handling information. */ /* -------------------------------------------------------------------- */ close_connection = msLayerGetProcessingKey( layer, "CLOSE_CONNECTION" ); if( close_connection == NULL ) close_connection = "NORMAL"; if( strcasecmp(close_connection,"NORMAL") == 0 ) conn->lifespan = MS_LIFE_ZEROREF; else if( strcasecmp(close_connection,"DEFER") == 0 ) conn->lifespan = MS_LIFE_FOREVER; else if( strcasecmp(close_connection,"ALWAYS") == 0 ) conn->lifespan = MS_LIFE_SINGLE; else { msDebug("msConnPoolRegister(): " "Unrecognised CLOSE_CONNECTION value '%s'\n", close_connection ); msSetError( MS_MISCERR, "Unrecognised CLOSE_CONNECTION value '%s'", "msConnPoolRegister()", close_connection ); conn->lifespan = MS_LIFE_ZEROREF; } msReleaseLock( TLOCK_POOL ); }
/* rebuild the clusters according to the current extent */ int RebuildClusters(layerObj *layer, int isQuery) { mapObj* map; layerObj* srcLayer; double distance, maxDistanceX, maxDistanceY, cellSizeX, cellSizeY; rectObj searchrect; int status; clusterInfo* current; int depth; #ifdef USE_CLUSTER_EXTERNAL int layerIndex; #endif msClusterLayerInfo* layerinfo = layer->layerinfo; if (!layerinfo) { msSetError(MS_MISCERR, "Layer is not open: %s", "RebuildClusters()", layer->name); return MS_FAILURE; } if (!layer->map) { msSetError(MS_MISCERR, "No map associated with this layer: %s", "RebuildClusters()", layer->name); return MS_FAILURE; } if (layer->debug >= MS_DEBUGLEVEL_VVV) msDebug("Clustering started.\n"); map = layer->map; layerinfo->current = layerinfo->finalized; /* restart */ /* check whether all shapes should be returned from a query */ if(msLayerGetProcessingKey(layer, "CLUSTER_GET_ALL_SHAPES") != NULL) layerinfo->get_all_shapes = MS_TRUE; else layerinfo->get_all_shapes = MS_FALSE; /* identify the current extent */ if(layer->transform == MS_TRUE) searchrect = map->extent; else { searchrect.minx = searchrect.miny = 0; searchrect.maxx = map->width-1; searchrect.maxy = map->height-1; } if (searchrect.minx == layerinfo->searchRect.minx && searchrect.miny == layerinfo->searchRect.miny && searchrect.maxx == layerinfo->searchRect.maxx && searchrect.maxy == layerinfo->searchRect.maxy) { /* already built */ return MS_SUCCESS; } /* destroy previous data*/ clusterDestroyData(layerinfo); layerinfo->searchRect = searchrect; /* reproject the rectangle to layer coordinates */ #ifdef USE_PROJ if((map->projection.numargs > 0) && (layer->projection.numargs > 0)) msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */ #endif /* determine the compare method */ layerinfo->fnCompare = CompareRectangleRegion; if (layer->cluster.region) { if (EQUAL(layer->cluster.region, "ellipse")) layerinfo->fnCompare = CompareEllipseRegion; } /* trying to find a reasonable quadtree depth */ depth = 0; distance = layer->cluster.maxdistance; while ((distance < map->width || distance < map->height) && depth <= TREE_MAX_DEPTH) { distance *= 2; ++depth; } layerinfo->depth = depth; cellSizeX = MS_CELLSIZE(searchrect.minx, searchrect.maxx, map->width); cellSizeY = MS_CELLSIZE(searchrect.miny, searchrect.maxy, map->height); maxDistanceX = layer->cluster.maxdistance * cellSizeX; maxDistanceY = layer->cluster.maxdistance * cellSizeY; /* increase the search rectangle so that the neighbouring shapes are also retrieved */ searchrect.minx -= layer->cluster.buffer * cellSizeX; searchrect.maxx += layer->cluster.buffer * cellSizeX; searchrect.miny -= layer->cluster.buffer * cellSizeY; searchrect.maxy += layer->cluster.buffer * cellSizeY; /* create the root node */ if (layerinfo->root) clusterTreeNodeDestroy(layerinfo, layerinfo->root); layerinfo->root = clusterTreeNodeCreate(layerinfo, searchrect); srcLayer = &layerinfo->srcLayer; /* start retrieving the shapes */ status = msLayerWhichShapes(srcLayer, searchrect, isQuery); if(status == MS_DONE) { /* no overlap */ return MS_SUCCESS; } else if(status != MS_SUCCESS) { return MS_FAILURE; } /* step through the source shapes and populate the quadtree with the tentative clusters */ if ((current = clusterInfoCreate(layerinfo)) == NULL) return MS_FAILURE; while((status = msLayerNextShape(srcLayer, ¤t->shape)) == MS_SUCCESS) { #if defined(USE_PROJ) && defined(USE_CLUSTER_EXTERNAL) /* transform the shape to the projection of this layer */ if(srcLayer->transform == MS_TRUE && srcLayer->project && layer->transform == MS_TRUE && layer->project &&msProjectionsDiffer(&(srcLayer->projection), &(layer->projection))) msProjectShape(&srcLayer->projection, &layer->projection, ¤t->shape); #endif /* set up positions and variance */ current->avgx = current->x = current->shape.bounds.minx; current->avgy = current->y = current->shape.bounds.miny; current->varx = current->vary = 0; /* set up the area of interest when searching for the neighboring shapes */ current->bounds.minx = current->x - maxDistanceX; current->bounds.miny = current->y - maxDistanceY; current->bounds.maxx = current->x + maxDistanceX; current->bounds.maxy = current->y + maxDistanceY; /* if the shape doesn't overlap we must skip it to avoid further issues */ if(!msRectOverlap(&searchrect, ¤t->bounds)) { msFreeShape(¤t->shape); msInitShape(¤t->shape); msDebug("Skipping an invalid shape falling outside of the given extent\n"); continue; } /* construct the item array */ if (layer->iteminfo) BuildFeatureAttributes(layer, layerinfo, ¤t->shape); /* evaluate the group expression */ if (layer->cluster.group.string) current->group = msClusterGetGroupText(&layer->cluster.group, ¤t->shape); /*start a query for the related shapes */ findRelatedShapes(layerinfo, layerinfo->root, current); /* add this shape to the tree */ if (treeNodeAddShape(layerinfo, layerinfo->root, current, depth) != MS_SUCCESS) { clusterInfoDestroyList(layerinfo, current); return MS_FAILURE; } if ((current = clusterInfoCreate(layerinfo)) == NULL) { clusterInfoDestroyList(layerinfo, current); return MS_FAILURE; } } clusterInfoDestroyList(layerinfo, current); while (layerinfo->root) { #ifdef TESTCOUNT int n; double avgx, avgy; #endif /* pick up the best cluster from the tree and do the finalization */ /* the initial rank must be big enough */ layerinfo->rank = (searchrect.maxx - searchrect.minx) * (searchrect.maxx - searchrect.minx) + (searchrect.maxy - searchrect.miny) * (searchrect.maxy - searchrect.miny) + 1; layerinfo->current = NULL; findBestCluster(layer, layerinfo, layerinfo->root); if (layerinfo->current == NULL) { if (layer->debug >= MS_DEBUGLEVEL_VVV) msDebug("Clustering terminated.\n"); break; /* completed */ } /* Update the feature count of the shape */ InitShapeAttributes(layer, layerinfo->current); /* collecting the shapes of the cluster */ collectClusterShapes(layerinfo, layerinfo->root, layerinfo->current); if (layer->debug >= MS_DEBUGLEVEL_VVV) { msDebug("processing cluster %p: rank=%lf fcount=%d ncoll=%d nfin=%d nfins=%d nflt=%d bounds={%lf %lf %lf %lf}\n", layerinfo->current, layerinfo->rank, layerinfo->current->numsiblings + 1, layerinfo->current->numcollected, layerinfo->numFinalized, layerinfo->numFinalizedSiblings, layerinfo->numFiltered, layerinfo->current->bounds.minx, layerinfo->current->bounds.miny, layerinfo->current->bounds.maxx, layerinfo->current->bounds.maxy); if (layerinfo->current->node) { char pszBuffer[TREE_MAX_DEPTH + 1]; clusterTreeNode* node = layerinfo->current->node; int position = node->position; int i = 1; while (position > 0 && i <= TREE_MAX_DEPTH) { pszBuffer[TREE_MAX_DEPTH - i] = '0' + (position % 4); position = position >> 2; ++i; } pszBuffer[TREE_MAX_DEPTH] = 0; msDebug(" ->node %p: count=%d index=%d pos=%s subn={%p %p %p %p} rect={%lf %lf %lf %lf}\n", node, node->numshapes, node->index, pszBuffer + TREE_MAX_DEPTH - i + 1, node->subnode[0], node->subnode[1], node->subnode[2], node->subnode[3], node->rect.minx, node->rect.miny, node->rect.maxx, node->rect.maxy); } }
/** * Generic function to render chart layers. */ int msDrawChartLayer(mapObj *map, layerObj *layer, imageObj *image) { rectObj searchrect; const char *chartTypeProcessingKey=msLayerGetProcessingKey( layer,"CHART_TYPE" ); int chartType=MS_CHART_TYPE_PIE; int status = MS_FAILURE; if (image && map && layer) { if( !(MS_RENDERER_PLUGIN(image->format) )) { msSetError(MS_MISCERR, "chart drawing currently only supports GD and AGG renderers", "msDrawChartLayer()"); return MS_FAILURE; } if( layer->numclasses < 2 ) { msSetError(MS_MISCERR,"chart drawing requires at least 2 classes in layer", "msDrawChartLayer()"); return MS_FAILURE; } if(chartTypeProcessingKey!=NULL) { if( strcasecmp(chartTypeProcessingKey,"PIE") == 0 ) { chartType=MS_CHART_TYPE_PIE; } else if( strcasecmp(chartTypeProcessingKey,"BAR") == 0 ) { chartType=MS_CHART_TYPE_BAR; } else if( strcasecmp(chartTypeProcessingKey,"VBAR") == 0 ) { chartType=MS_CHART_TYPE_VBAR; } else { msSetError(MS_MISCERR,"unknown chart type for processing key \"CHART_TYPE\", must be one of \"PIE\" or \"BAR\"", "msDrawChartLayer()"); return MS_FAILURE; } } if(chartType == MS_CHART_TYPE_PIE) { pieLayerProcessDynamicDiameter(layer); } /* open this layer */ status = msLayerOpen(layer); if(status != MS_SUCCESS) return MS_FAILURE; status = msLayerWhichItems(layer, MS_FALSE, NULL); if(status != MS_SUCCESS) { msLayerClose(layer); return MS_FAILURE; } /* identify target shapes */ if(layer->transform == MS_TRUE) searchrect = map->extent; else { searchrect.minx = searchrect.miny = 0; searchrect.maxx = map->width-1; searchrect.maxy = map->height-1; } #ifdef USE_PROJ if((map->projection.numargs > 0) && (layer->projection.numargs > 0)) msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */ #endif status = msLayerWhichShapes(layer, searchrect, MS_FALSE); if(status == MS_DONE) { /* no overlap */ msLayerClose(layer); return MS_SUCCESS; } else if(status != MS_SUCCESS) { msLayerClose(layer); return MS_FAILURE; } switch(chartType) { case MS_CHART_TYPE_PIE: status = msDrawPieChartLayer(map, layer, image); break; case MS_CHART_TYPE_BAR: status = msDrawBarChartLayer(map, layer, image); break; case MS_CHART_TYPE_VBAR: status = msDrawVBarChartLayer(map, layer, image); break; default: return MS_FAILURE;/*shouldn't be here anyways*/ } msLayerClose(layer); } return status; }
int msDrawBarChartLayer(mapObj *map, layerObj *layer, imageObj *image) { shapeObj shape; int status=MS_SUCCESS; const char *chartSizeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE" ); const char *barMax=msLayerGetProcessingKey( layer,"CHART_BAR_MAXVAL" ); const char *barMin=msLayerGetProcessingKey( layer,"CHART_BAR_MINVAL" ); float width,height; float barWidth; float *values; styleObj **styles; pointObj center; float barMaxVal,barMinVal; int numvalues = layer->numclasses; if(chartSizeProcessingKey==NULL) { width=height=20; } else { switch(sscanf(chartSizeProcessingKey ,"%f %f",&width,&height)) { case 2: break; case 1: height = width; break; default: msSetError(MS_MISCERR, "msDrawChart format error for processing key \"CHART_SIZE\"", "msDrawBarChartLayer()"); return MS_FAILURE; } } if(barMax) { if(sscanf(barMax,"%f",&barMaxVal)!=1) { msSetError(MS_MISCERR, "Error reading value for processing key \"CHART_BAR_MAXVAL\"", "msDrawBarChartLayer()"); return MS_FAILURE; } } if(barMin) { if(sscanf(barMin,"%f",&barMinVal)!=1) { msSetError(MS_MISCERR, "Error reading value for processing key \"CHART_BAR_MINVAL\"", "msDrawBarChartLayer()"); return MS_FAILURE; } } if(barMin && barMax && barMinVal>=barMaxVal) { msSetError(MS_MISCERR, "\"CHART_BAR_MINVAL\" must be less than \"CHART_BAR_MAXVAL\"", "msDrawBarChartLayer()"); return MS_FAILURE; } barWidth=(float)width/(float)layer->numclasses; if(!barWidth) { msSetError(MS_MISCERR, "Specified width of chart too small to fit given number of classes", "msDrawBarChartLayer()"); return MS_FAILURE; } msInitShape(&shape); values=(float*)calloc(numvalues,sizeof(float)); MS_CHECK_ALLOC(values, numvalues*sizeof(float), MS_FAILURE); styles = (styleObj**)malloc(numvalues*sizeof(styleObj*)); if (styles == NULL) { msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msDrawBarChartLayer()", __FILE__, __LINE__, numvalues*sizeof(styleObj*)); free(values); return MS_FAILURE; } while(MS_SUCCESS == getNextShape(map,layer,values,styles,&shape)) { msDrawStartShape(map, layer, image, &shape); if(findChartPoint(map, &shape, width,height, ¢er)==MS_SUCCESS) { status = msDrawBarChart(map,image, ¢er, values, styles, numvalues, width,height, (barMax!=NULL)?&barMaxVal:NULL, (barMin!=NULL)?&barMinVal:NULL, barWidth); } msDrawEndShape(map,layer,image,&shape); msFreeShape(&shape); } free(values); free(styles); return status; }
int msDrawVBarChartLayer(mapObj *map, layerObj *layer, imageObj *image) { shapeObj shape; int status=MS_SUCCESS; const char *chartSizeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE" ); const char *chartScaleProcessingKey=msLayerGetProcessingKey( layer,"CHART_SCALE" ); float barWidth,scale=1.0; float *values; styleObj **styles; pointObj center; int numvalues = layer->numclasses; if(chartSizeProcessingKey==NULL) { barWidth=20; } else { if(sscanf(chartSizeProcessingKey ,"%f",&barWidth) != 1) { msSetError(MS_MISCERR, "msDrawChart format error for processing key \"CHART_SIZE\"", "msDrawVBarChartLayer()"); return MS_FAILURE; } } if(chartScaleProcessingKey) { if(sscanf(chartScaleProcessingKey,"%f",&scale)!=1) { msSetError(MS_MISCERR, "Error reading value for processing key \"CHART_SCALE\"", "msDrawVBarChartLayer()"); return MS_FAILURE; } } msInitShape(&shape); values=(float*)calloc(numvalues,sizeof(float)); MS_CHECK_ALLOC(values, numvalues*sizeof(float), MS_FAILURE); styles = (styleObj**)malloc(numvalues*sizeof(styleObj*)); if (styles == NULL) { msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msDrawVBarChartLayer()", __FILE__, __LINE__, numvalues*sizeof(styleObj*)); free(values); return MS_FAILURE; } while(MS_SUCCESS == getNextShape(map,layer,values,styles,&shape)) { int i; double h=0; for(i=0; i<numvalues; i++) { values[i]*=scale; h += values[i]; } msDrawStartShape(map, layer, image, &shape); if(findChartPoint(map, &shape, barWidth,h, ¢er)==MS_SUCCESS) { status = msDrawVBarChart(map,image, ¢er, values, styles, numvalues, barWidth); } msDrawEndShape(map,layer,image,&shape); msFreeShape(&shape); } free(values); free(styles); return status; }
int msDrawPieChartLayer(mapObj *map, layerObj *layer, imageObj *image) { shapeObj shape; int status=MS_SUCCESS; const char *chartRangeProcessingKey=NULL; const char *chartSizeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE" ); float diameter, mindiameter=-1, maxdiameter, minvalue, maxvalue; float *values; styleObj **styles; pointObj center; int numvalues = layer->numclasses; /* the number of classes to represent in the graph */ if(chartSizeProcessingKey==NULL) { chartRangeProcessingKey=msLayerGetProcessingKey( layer,"CHART_SIZE_RANGE" ); if(chartRangeProcessingKey==NULL) diameter=20; else { sscanf(chartRangeProcessingKey,"%*s %f %f %f %f", &mindiameter,&maxdiameter,&minvalue,&maxvalue); } } else { if(sscanf(chartSizeProcessingKey ,"%f",&diameter)!=1) { msSetError(MS_MISCERR, "msDrawChart format error for processing key \"CHART_SIZE\"", "msDrawPieChartLayer()"); return MS_FAILURE; } } /* step through the target shapes */ msInitShape(&shape); values=(float*)calloc(numvalues,sizeof(float)); MS_CHECK_ALLOC(values, numvalues*sizeof(float), MS_FAILURE); styles = (styleObj**)malloc((numvalues)*sizeof(styleObj*)); if (styles == NULL) { msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msDrawPieChartLayer()", __FILE__, __LINE__, numvalues*sizeof(styleObj*)); free(values); return MS_FAILURE; } if(chartRangeProcessingKey!=NULL) numvalues--; while(MS_SUCCESS == getNextShape(map,layer,values,styles,&shape)) { msDrawStartShape(map, layer, image, &shape); if(chartRangeProcessingKey!=NULL) { diameter = values[numvalues]; if(mindiameter>=0) { if(diameter<=minvalue) diameter=mindiameter; else if(diameter>=maxvalue) diameter=maxdiameter; else { diameter=MS_NINT( mindiameter+ ((diameter-minvalue)/(maxvalue-minvalue))* (maxdiameter-mindiameter) ); } } } if(findChartPoint(map, &shape, diameter, diameter, ¢er) == MS_SUCCESS) { status = msDrawPieChart(map,image, ¢er, diameter, values,styles,numvalues); } msDrawEndShape(map,layer,image,&shape); msFreeShape(&shape); } free(values); free(styles); return status; }