func()

in sweet/generators/tile38.go [47:219]


func (_ Tile38) Generate(cfg *common.GenConfig) error {
	if cfg.AssetsDir != cfg.OutputDir {
		// Copy over the datasets which are used to generate
		// the server's persistent data.
		if err := os.MkdirAll(filepath.Join(cfg.OutputDir, "gen-data", "geonames"), 0755); err != nil {
			return err
		}
		if err := os.MkdirAll(filepath.Join(cfg.OutputDir, "gen-data", "datahub"), 0755); err != nil {
			return err
		}
		err := copyFiles(cfg.OutputDir, cfg.AssetsDir, []string{
			"gen-data/geonames/allCountries.txt",
			"gen-data/geonames/LICENSE",
			"gen-data/datahub/countries.geojson",
			"gen-data/datahub/LICENSE",
			"gen-data/README.md",
		})
		if err != nil {
			return err
		}
	}

	// Create a temporary directory where we can put the Tile38
	// source and build it.
	tmpDir, err := ioutil.TempDir("", "tile38-gen")
	if err != nil {
		return err
	}

	// In order to generate the assets, we need a working Tile38
	// server. Use the harness code to get the source.
	srcDir := filepath.Join(tmpDir, "src")
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	if err := (harnesses.Tile38{}).Get(srcDir); err != nil {
		return err
	}

	// Add the Go tool to PATH, since tile38's Makefile doesn't provide enough
	// visibility into how tile38 is built to allow us to pass this information
	// directly.
	env := cfg.GoTool.Env.Prefix("PATH", filepath.Join(filepath.Dir(cfg.GoTool.Tool))+":")

	// Build Tile38.
	cmd := exec.Command("make", "-C", srcDir)
	cmd.Env = env.Collapse()
	if err := cmd.Run(); err != nil {
		return err
	}

	// Launch the server.
	//
	// Generate the datastore in the tmp directory and copy it
	// over later, otherwise if cfg.OutputDir == cfg.AssetsDir, then
	// we might launch the server with an old database.
	serverPath := filepath.Join(srcDir, "tile38-server")
	tmpDataPath := filepath.Join(srcDir, "tile38-data")
	var buf bytes.Buffer
	srvCmd, err := launchServer(serverPath, tmpDataPath, &buf)
	if err != nil {
		log.Printf("=== Server stdout+stderr ===")
		for _, line := range strings.Split(buf.String(), "\n") {
			log.Printf(line)
		}
		return fmt.Errorf("error: starting server: %v", err)
	}

	// Clean up the server process after we're done.
	defer func() {
		if r := srvCmd.Process.Signal(os.Interrupt); r != nil {
			if err == nil {
				err = r
			} else {
				fmt.Fprintf(os.Stderr, "failed to shut down server: %v\n", r)
			}
			return
		}
		if _, r := srvCmd.Process.Wait(); r != nil {
			if err == nil {
				err = r
			} else if r != nil {
				fmt.Fprintf(os.Stderr, "failed to wait for server to exit: %v\n", r)
			}
			return
		}
		if err != nil && buf.Len() != 0 {
			log.Printf("=== Server stdout+stderr ===")
			for _, line := range strings.Split(buf.String(), "\n") {
				log.Printf(line)
			}
		}
		if err == nil {
			// Copy database to the output directory.
			// We cannot do this until we've stopped the
			// server because the data might not have been
			// written back yet. An interrupt should have
			// the server shut down gracefully.
			err = fileutil.CopyDir(
				filepath.Join(cfg.OutputDir, "data"),
				tmpDataPath,
			)
		}
	}()

	// Connect to the server and feed it data.
	c, err := redis.Dial("tcp", ":9851")
	if err != nil {
		return err
	}
	defer c.Close()

	// Store GeoJSON of countries.
	genDataDir := filepath.Join(cfg.AssetsDir, "gen-data")
	if err := storeGeoJSON(c, filepath.Join(genDataDir, "datahub", "countries.geojson")); err != nil {
		return err
	}

	// Feed the server points-of-interest.
	f, err := os.Open(filepath.Join(genDataDir, "geonames", "allCountries.txt"))
	if err != nil {
		return err
	}
	defer f.Close()

	// allCountries.txt is a TSV file with a fixed number ofcolumns per row
	// (line). What we need to pull out of it is a unique ID, and the
	// coordinates for the point-of-interest.
	const (
		columnsPerLine = 19
		idColumn       = 0
		latColumn      = 4
		lonColumn      = 5
	)
	s := tsvScanner(f)

	var item int
	var obj geoObj
	for s.Scan() {
		// Each iteration of this loop is another cell in the
		// TSV file.
		switch item % columnsPerLine {
		case idColumn:
			i, err := strconv.ParseInt(s.Text(), 10, 64)
			if err != nil {
				return err
			}
			obj.id = i
		case latColumn:
			f, err := strconv.ParseFloat(s.Text(), 64)
			if err != nil {
				return err
			}
			obj.lat = f
		case lonColumn:
			f, err := strconv.ParseFloat(s.Text(), 64)
			if err != nil {
				return err
			}
			obj.lon = f
		}
		item++

		// We finished off another row, which means obj
		// should be correctly populated.
		if item%columnsPerLine == 0 {
			if err := storeGeoObj(c, &obj); err != nil {
				return err
			}
		}
	}
	return s.Err()
}