使用Golang Draw2D时极大的SVG文件
我一直在使用库 draw2d with go-nexrad ,我能够生成png和svg文件。但是,生成的SVG文件是巨大的,其中不到10MB PNG文件成为具有相同选项的28MB SVG文件。这是代码:
func render(out string, radials []*archive2.Message31, label string) {
width := float64(imageSize)
height := float64(imageSize)
PNGcanvas := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
draw.Draw(PNGcanvas, PNGcanvas.Bounds(), image.Black, image.ZP, draw.Src)
PNGgc := draw2dimg.NewGraphicContext(PNGcanvas)
SVGcanvas := draw2dsvg.NewSvg()
SVGcanvas.Width = strconv.Itoa(int(width)) + "px"
SVGcanvas.Height = strconv.Itoa(int(width)) + "px"
SVGgc := draw2dsvg.NewGraphicContext(SVGcanvas)
xc := width / 2
yc := height / 2
pxPerKm := width / 2 / 460
firstGatePx := float64(radials[0].ReflectivityData.DataMomentRange) / 1000 * pxPerKm
gateIntervalKm := float64(radials[0].ReflectivityData.DataMomentRangeSampleInterval) / 1000
gateWidthPx := gateIntervalKm * pxPerKm
t := time.Now()
log.Println("rendering radials")
// valueDist := map[float32]int{}
for _, radial := range radials {
// round to the nearest rounded azimuth for the given resolution.
// ex: for radial 20.5432, round to 20.5
azimuthAngle := float64(radial.Header.AzimuthAngle) - 90
if azimuthAngle < 0 {
azimuthAngle = 360.0 + azimuthAngle
}
azimuthSpacing := radial.Header.AzimuthResolutionSpacing()
azimuth := math.Floor(azimuthAngle)
if math.Floor(azimuthAngle+azimuthSpacing) > azimuth {
azimuth += azimuthSpacing
}
startAngle := azimuth * (math.Pi / 180.0) /* angles are specified */
endAngle := azimuthSpacing * (math.Pi / 180.0) /* clockwise in radians */
// start drawing gates from the start of the first gate
distanceX, distanceY := firstGatePx, firstGatePx
if vectorize == "png" {
PNGgc.SetLineWidth(gateWidthPx + 1)
} else if vectorize == "svg" {
SVGgc.SetLineWidth(gateWidthPx + 1)
}
if vectorize == "png" {
PNGgc.SetLineCap(draw2d.ButtCap)
} else if vectorize == "svg" {
SVGgc.SetLineCap(draw2d.ButtCap)
}
var gates []float32
switch product {
case "vel":
gates = radial.VelocityData.ScaledData()
case "sw":
gates = radial.SwData.ScaledData()
case "rho":
gates = radial.RhoData.ScaledData()
default:
gates = radial.ReflectivityData.ScaledData()
}
numGates := len(gates)
for i, v := range gates {
if v != archive2.MomentDataBelowThreshold {
//fmt.Println(gateWidthPx)
if i == 0 {
SVGgc.SetLineWidth(0)
} else if i > 0 {
SVGgc.SetLineWidth(gateWidthPx + 1)
}
// valueDist[v] += 1
if vectorize == "png" {
PNGgc.MoveTo(xc+math.Cos(startAngle)*distanceX, yc+math.Sin(startAngle)*distanceY)
} else if vectorize == "svg" {
SVGgc.MoveTo(xc+math.Cos(startAngle)*distanceX, yc+math.Sin(startAngle)*distanceY)
}
// make the gates connect visually by extending arcs so there is no space between adjacent gates.
if i == 0 {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle-.001, endAngle+.001)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle-.001, endAngle+.001)
}
} else if i == numGates-1 {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle)
}
} else {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle+.001)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle+.001)
}
}
if vectorize == "png" {
PNGgc.SetStrokeColor(colorSchemes[product][colorScheme](v))
} else if vectorize == "svg" {
SVGgc.SetStrokeColor(colorSchemes[product][colorScheme](v))
}
if vectorize == "png" {
PNGgc.Stroke()
} else if vectorize == "svg" {
SVGgc.Stroke()
}
}
distanceX += gateWidthPx
distanceY += gateWidthPx
azimuth += radial.Header.AzimuthResolutionSpacing()
}
}
// fmt.Println(valueDist)
if renderLabel {
if vectorize == "png" {
addLabel(PNGcanvas, int(width-495.0), int(height-10.0), label)
} else if vectorize == "svg" {
logrus.Warn("Labels cannot be drawn on an SVG image, ignoring -L flag")
}
}
// Save to file
if vectorize == "png" {
draw2dimg.SaveToPngFile(out, PNGcanvas)
fmt.Println("Finished in", time.Since(t))
} else if vectorize == "svg" {
draw2dsvg.SaveToSvgFile(out, SVGcanvas)
fmt.Println("Finished in", time.Since(t))
}
}
完整文件可以在我的项目的叉子中找到在这里。
我认为SVG之所以如此之大的原因是因为它可能通过尝试渲染每个像素而不仅仅是一个起点和终点来生成文件效率非常低。我尝试使用svggc.setdpi()
设置DPI,但这尚不起作用。
如果有人对文件如此之大,或者对如何修复该文件有任何了解,我将非常感谢您的意见。希望您不必浏览整个go-nexrad
才能理解这一点,我已经包括了我几乎确定要引起的代码块,并且是使用库的部分。
I have been using the library draw2d with go-nexrad, and I am able to generate PNG and SVG files. However, the SVG files that are generated are enormous, with a less than 10MB PNG file becoming a 28MB SVG file with the same options. Here is the code:
func render(out string, radials []*archive2.Message31, label string) {
width := float64(imageSize)
height := float64(imageSize)
PNGcanvas := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
draw.Draw(PNGcanvas, PNGcanvas.Bounds(), image.Black, image.ZP, draw.Src)
PNGgc := draw2dimg.NewGraphicContext(PNGcanvas)
SVGcanvas := draw2dsvg.NewSvg()
SVGcanvas.Width = strconv.Itoa(int(width)) + "px"
SVGcanvas.Height = strconv.Itoa(int(width)) + "px"
SVGgc := draw2dsvg.NewGraphicContext(SVGcanvas)
xc := width / 2
yc := height / 2
pxPerKm := width / 2 / 460
firstGatePx := float64(radials[0].ReflectivityData.DataMomentRange) / 1000 * pxPerKm
gateIntervalKm := float64(radials[0].ReflectivityData.DataMomentRangeSampleInterval) / 1000
gateWidthPx := gateIntervalKm * pxPerKm
t := time.Now()
log.Println("rendering radials")
// valueDist := map[float32]int{}
for _, radial := range radials {
// round to the nearest rounded azimuth for the given resolution.
// ex: for radial 20.5432, round to 20.5
azimuthAngle := float64(radial.Header.AzimuthAngle) - 90
if azimuthAngle < 0 {
azimuthAngle = 360.0 + azimuthAngle
}
azimuthSpacing := radial.Header.AzimuthResolutionSpacing()
azimuth := math.Floor(azimuthAngle)
if math.Floor(azimuthAngle+azimuthSpacing) > azimuth {
azimuth += azimuthSpacing
}
startAngle := azimuth * (math.Pi / 180.0) /* angles are specified */
endAngle := azimuthSpacing * (math.Pi / 180.0) /* clockwise in radians */
// start drawing gates from the start of the first gate
distanceX, distanceY := firstGatePx, firstGatePx
if vectorize == "png" {
PNGgc.SetLineWidth(gateWidthPx + 1)
} else if vectorize == "svg" {
SVGgc.SetLineWidth(gateWidthPx + 1)
}
if vectorize == "png" {
PNGgc.SetLineCap(draw2d.ButtCap)
} else if vectorize == "svg" {
SVGgc.SetLineCap(draw2d.ButtCap)
}
var gates []float32
switch product {
case "vel":
gates = radial.VelocityData.ScaledData()
case "sw":
gates = radial.SwData.ScaledData()
case "rho":
gates = radial.RhoData.ScaledData()
default:
gates = radial.ReflectivityData.ScaledData()
}
numGates := len(gates)
for i, v := range gates {
if v != archive2.MomentDataBelowThreshold {
//fmt.Println(gateWidthPx)
if i == 0 {
SVGgc.SetLineWidth(0)
} else if i > 0 {
SVGgc.SetLineWidth(gateWidthPx + 1)
}
// valueDist[v] += 1
if vectorize == "png" {
PNGgc.MoveTo(xc+math.Cos(startAngle)*distanceX, yc+math.Sin(startAngle)*distanceY)
} else if vectorize == "svg" {
SVGgc.MoveTo(xc+math.Cos(startAngle)*distanceX, yc+math.Sin(startAngle)*distanceY)
}
// make the gates connect visually by extending arcs so there is no space between adjacent gates.
if i == 0 {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle-.001, endAngle+.001)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle-.001, endAngle+.001)
}
} else if i == numGates-1 {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle)
}
} else {
if vectorize == "png" {
PNGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle+.001)
} else if vectorize == "svg" {
SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle+.001)
}
}
if vectorize == "png" {
PNGgc.SetStrokeColor(colorSchemes[product][colorScheme](v))
} else if vectorize == "svg" {
SVGgc.SetStrokeColor(colorSchemes[product][colorScheme](v))
}
if vectorize == "png" {
PNGgc.Stroke()
} else if vectorize == "svg" {
SVGgc.Stroke()
}
}
distanceX += gateWidthPx
distanceY += gateWidthPx
azimuth += radial.Header.AzimuthResolutionSpacing()
}
}
// fmt.Println(valueDist)
if renderLabel {
if vectorize == "png" {
addLabel(PNGcanvas, int(width-495.0), int(height-10.0), label)
} else if vectorize == "svg" {
logrus.Warn("Labels cannot be drawn on an SVG image, ignoring -L flag")
}
}
// Save to file
if vectorize == "png" {
draw2dimg.SaveToPngFile(out, PNGcanvas)
fmt.Println("Finished in", time.Since(t))
} else if vectorize == "svg" {
draw2dsvg.SaveToSvgFile(out, SVGcanvas)
fmt.Println("Finished in", time.Since(t))
}
}
The full file can be found in my fork of the project here.
The reason I think the SVG is so large is because it is generating the file very inefficiently, possibly by trying to render every pixel instead of just a start and end point. I have tried setting the DPI with SVGgc.setDPI()
, but that hasn't worked.
If anyone has any idea about why the file is so large, or any idea of how to fix it, I would greatly appreciate your input. Hopefully you won't have to go through the entire go-nexrad
project to understand this, I have included the code block that I am almost certain is causing the issue, and is the part that uses the library.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论