// 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 }
// 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 }
func backend(c appengine.Context) string { // Use the load-balanced hostname for the "datagrabber" backend. return "http://" + appengine.BackendHostname(c, "datagrabber", -1) }