Example #1
0
// checkDone tests whether we have generated all the tiles.
// If so, it creates a zip task on the zipper backend.
func checkDone(c appengine.Context, oKey *datastore.Key) (done bool, err error) {
	// Create a zip task if we're done and one hasn't been created already.
	tx := func(c appengine.Context) error {
		o := new(Overlay)
		err := datastore.Get(c, oKey, o)
		if err != nil {
			return err
		}

		// Check whether we have generated all the tiles.
		count, err := datastore.NewQuery("Tile").Ancestor(oKey).Count(c)
		if err != nil {
			return err
		}
		done = o.Tiles >= count
		if !done || o.Zip != "" {
			return nil
		}

		// Create a task to build the zip file,
		// targeting the zipper backend.
		task := taskqueue.NewPOSTTask("/zip", url.Values{
			"key": {oKey.Encode()},
		})
		if !appengine.IsDevAppServer() {
			host := appengine.BackendHostname(c, zipBackend, -1)
			task.Header.Set("Host", host)
		}
		if _, err := taskqueue.Add(c, task, zipQueue); err != nil {
			return err
		}

		// Store a sentinel value in Zip field to prevent a
		// second zip task from being created.
		// This value will be overwritten by the zip task.
		o.Zip = zipSentinel
		_, err = datastore.Put(c, oKey, o)
		return err
	}
	if err := datastore.RunInTransaction(c, tx, nil); err != nil {
		return false, err
	}
	return done, nil
}
Example #2
0
// processHandler initiates the processing of an Overlay, including kicking off
// appropriate slice tasks.
func processHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) *appError {
	if r.Method != "POST" {
		return &appError{nil, "must use POST", http.StatusMethodNotAllowed}
	}

	// Get the Overlay from the datastore.
	k, o, err := getOverlay(r)
	if err != nil {
		return appErrorf(err, "overlay not found")
	}

	// Process the request.
	if o.TopLeft, err = parsePair(r.FormValue("topLeft")); err != nil {
		return appErrorf(err, "invalid parameter topLeft")
	}
	if o.TopRight, err = parsePair(r.FormValue("topRight")); err != nil {
		return appErrorf(err, "invalid parameter topLeft")
	}
	if o.BottomRight, err = parsePair(r.FormValue("bottomRight")); err != nil {
		return appErrorf(err, "invalid parameter bottomRight")
	}

	// Compute the transformation matrix.
	a := graphics.I.Scale(1/float64(o.Width), 1/float64(o.Height)).
		Mul(inverse(graphics.Affine{
			o.TopRight[0] - o.TopLeft[0], o.BottomRight[0] - o.TopRight[0], o.TopLeft[0],
			o.TopRight[1] - o.TopLeft[1], o.BottomRight[1] - o.TopRight[1], o.TopLeft[1],
			0, 0, 1,
		}))
	o.Transform = []float64(a[:])

	// TODO(cbro): get min/max zoom from user.
	o.MinZoom = 0
	o.MaxZoom = 21

	// Compute tiles to be generated.
	var tiles []*Tile
	for zoom := o.MinZoom; zoom <= o.MaxZoom; zoom++ {
		tiles = append(tiles, tilesForZoom(o, zoom)...)
	}
	o.Tiles = len(tiles)

	// Create a channel between the app and the client's browser.
	token, err := channel.Create(c, k.Encode())
	if err != nil {
		return appErrorf(err, "couldn't create browser channel")
	}

	// Put the updated Overlay into the datastore.
	if _, err := datastore.Put(c, k, o); err != nil {
		return appErrorf(err, "could not save overlay to datastore")
	}

	// Create tasks to generate tiles.
	tasks := tileTasks(k.Encode(), tiles)
	if err := addTasks(c, tasks, tileQueue); err != nil {
		return appErrorf(err, "could not start tiling process")
	}

	// Create task to start slice process.
	task := taskqueue.NewPOSTTask("/slice", url.Values{"key": {k.Encode()}})
	for i := 0; i < sliceBackends; i++ {
		host := appengine.BackendHostname(c, sliceBackend, i)
		task.Header.Set("Host", host)
		if _, err := taskqueue.Add(c, task, sliceQueue); err != nil {
			return appErrorf(err, "could not start tiling process")
		}
	}

	// Send channel token as response.
	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(token))
	return nil
}
Example #3
0
func backend(c appengine.Context) string {
	// Use the load-balanced hostname for the "datagrabber" backend.
	return "http://" + appengine.BackendHostname(c, "datagrabber", -1)
}