diff options
| -rw-r--r-- | location.go | 95 | ||||
| -rw-r--r-- | main.go | 117 |
2 files changed, 212 insertions, 0 deletions
diff --git a/location.go b/location.go new file mode 100644 index 0000000..a9cc351 --- /dev/null +++ b/location.go @@ -0,0 +1,95 @@ +package main + +import ( + "fmt" + "github.com/flopp/go-staticmaps" + "github.com/fogleman/gg" + "github.com/golang/geo/s2" + qrcode "github.com/skip2/go-qrcode" + "image/color" + "log" + "os" + "path/filepath" +) + +type Location struct { + Number string `csv:"number"` + Name string `csv:"name"` + AltName string `csv:"altname"` + Latitude float64 `csv:"latitude"` + Longitude float64 `csv:"longitude"` + Comments string `csv:"comments"` +} + +func (l *Location) getQRCode() string { + log.Printf("Getting QR code for %s", l.Number) + path := filepath.Join(c.cache, fmt.Sprintf("%s-qrc.png", l.Number)) + old := true + fi, err := os.Stat(path) + if err == nil && fi.ModTime().After(c.cDate) { + old = false + if c.debug { + log.Printf("Using existing map file %s", path) + } + } + if old { + url := fmt.Sprintf("geo://%s,%s", l.Latitude, l.Longitude) + qrcode, err := qrcode.New(url, qrcode.Medium) + if err != nil { + log.Fatal("Failed to create QR code: %s", err) + } + err = qrcode.WriteFile(100, path) + if err != nil { + log.Fatal("Failed to write QR code: %s", err) + } + } + return path +} + +func (l *Location) getMap() string { + log.Printf("Getting map for %s", l.Number) + // Flag + old := true + path := filepath.Join(c.cache, fmt.Sprintf("%s-map.png", l.Number)) + fi, err := os.Stat(path) + if err == nil && fi.ModTime().After(c.cDate) { + old = false + if c.debug { + log.Printf("Using existing map file %s", path) + } + } + + if old { + if c.debug { + log.Printf("Creating new map file %s", path) + } + ctx := sm.NewContext() + ctx.SetSize(640, 513) + // ctx.SetSize(1280, 1026) + pos := s2.LatLngFromDegrees(l.Latitude, l.Longitude) + kh := s2.LatLngFromDegrees(19.89830, 99.81805) + ctx.AddMarker(sm.NewMarker(pos, color.RGBA{0xff, 0, 0, 0xff}, 16.0)) + ctx.AddMarker(sm.NewMarker(kh, color.RGBA{0, 0, 0xff, 0xff}, 16.0)) + // ctx.SetCenter(s2.LatLngFromDegrees(19.89830, 99.81805)) + // ctx.SetCenter(pos) + // ctx.SetTileProvider(sm.NewTileProviderOpenTopoMap()) + ctx.SetTileProvider(sm.NewTileProviderThunderforestOutdoors()) + // ctx.SetTileProvider(sm.NewTileProviderThunderforestLandscape()) + + if c.debug { + log.Printf("Rendering map for %s", l.Number) + } + img, err := ctx.Render() + if err != nil { + log.Fatal("Failed to render map: ", err) + } + if c.debug { + log.Printf("Saving map for %s", l.Number) + } + err = gg.SavePNG(path, img) + if err != nil { + log.Fatal("Failed to create image: ", err) + } + } + return path +} @@ -0,0 +1,117 @@ +package main + +import ( + "flag" + "github.com/gocarina/gocsv" + "github.com/jung-kurt/gofpdf" + "log" + "os" + "time" +) + +const ( + pWidth = 148 + pHeight = 105 +) + +var c struct { + in string + out string + cache string + cDate time.Time + debug bool +} + +func main() { + flag.StringVar(&c.in, "in", "", "Input CSV file") + flag.StringVar(&c.out, "out", "", "Output PDF file") + flag.StringVar(&c.cache, "cache", "cache", "Cache path") + flag.BoolVar(&c.debug, "debug", false, "Debug output") + // flag.StringVar(&c.outPrefix, "outPrefix", "", "Output file prefix") + + flag.Parse() + + if c.in == "" { + log.Fatal("Missing input file") + } + if c.out == "" { + c.out = "output.pdf" + } + + if c.debug { + log.Printf("Reading file %s", c.in) + } + f, err := os.Open(c.in) + if err != nil { + log.Fatal("Error opening file: %s", err) + } + defer f.Close() + locations := []*Location{} + + err = gocsv.UnmarshalFile(f, &locations) + if err != nil { + log.Fatal("Error parsing CSV: ", err) + } + fi, err := os.Stat(c.in) + if err != nil { + log.Fatal("Error statting CSV: ", err) + } + c.cDate = fi.ModTime() + + log.Printf("Processing %d locations", len(locations)) + + err = os.MkdirAll(c.cache, os.ModePerm) + if err != nil { + log.Fatal("Failed to create cache: ", err) + } + + // Generate the PDF + pdf := gofpdf.NewCustom(&gofpdf.InitType{ + OrientationStr: "L", + UnitStr: "mm", + Size: gofpdf.SizeType{Wd: pHeight, Ht: pWidth}, + }) + pdf.AddFont("Norasi", "", "Norasi.json") + pdf.SetFont("Norasi", "", 16) + pdf.SetTextColor(0, 0, 0) + tr := pdf.UnicodeTranslatorFromDescriptor("cp874") + + // Generate the PDF + for i := range locations { + loc := locations[i] + log.Printf("Processing map number %s %s", loc.Number, loc.Name) + pdf.AddPage() + + imageOpts := gofpdf.ImageOptions{"PNG", true} + + // QR Code + qrPath := loc.getQRCode() + // qrInfo := pdf.RegisterImageOptions(qrPath, imageOpts) + + // Map + smPath := loc.getMap() + // mapInfo := pdf.RegisterImageOptions(smPath, imageOpts) + + // Name + if loc.Name != "" { + pdf.TransformBegin() + pdf.TransformRotate(90, 5, pHeight-5) + pdf.SetFontSize(40) + pdf.Text(5, pHeight+5, loc.Number) + pdf.SetFontSize(20) + pdf.Text(5, pHeight+5+7, tr(loc.Name)) + pdf.ImageOptions(qrPath, pHeight-27, pHeight-10, 0, 0, false, imageOpts, 0, "") + pdf.TransformEnd() + } + + pdf.ImageOptions(smPath, 25, 5, 0, pHeight-10, false, imageOpts, 0, "") + if c.debug { + log.Printf("Added map %s", smPath) + } + } + + err = pdf.OutputFileAndClose(c.out) + if err != nil { + log.Fatal("Error generating PDF: ", err) + } +} |
