Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
133 lines (109 sloc) 4.66 KB
Base requirements:
- Client is able to save tie point picker interface state
- (State includes tie points and transform type and parameters)
- Client is able to retrieve tie point picker interface state
- Client is able to save image to overlay
- Could be a very large image
- Client is able to retrieve image to overlay
- (Nice to have, but note that usually the client will want tiles instead)
- Client is able to retrieve subtiles of overlay image in Google Maps API quadtree tile format
- Support both:
- Original image (for tie point interface)
- Warped image (for use as a map layer after tie point picking is done)
- Better if client fetches tiles statically from 'data' directory to avoid Django overhead
- Client is able to request image warping according to current transform parameters
- Note: warping might be updated several times depending on user interaction
- To ensure atomicity, generate new version in new directory, then rename directories
- Implement both command structure in JSON as well
as in URL structure
Refer to
* /overlay/
* GET:
* returns an index page of all the overlays
* click on entry, takes you to editor
* separate link to new overlay page
* n/a, http 405
* /overlay/<id>/delete/
* GET:
* returns confirmation page
* deletes objects
* redirects to index
* /overlay/new/
* GET:
* returns an image upload form (html)
* standard django form posting view, accepts multipart form
* returns human readable html response
* /overlay/<id>.json
* GET: retrieve overlay interface state (json)
* POST: save overlay interface state (json)
* /overlay/<id>/
* GET: retrieves tie point picker user interface for overlay (html)
* POST: n/a, http 405
* /overlay/<id>/warp/
* GET:
* returns a form with a button. clicking the button does the warp (html)
* interface code in the page submits the form via ajax
* POST: requests server apply current warping to the overlay image
* (empty params, uses transform from last save)
* /overlay/<id>/image/<anyFileName>
* GET: retrieves entire overlay image (e.g. image/png)
* POST: n/a, http 405
* [some static url in 'data' directory]
* directory -- image tiles are stored under the directory in Google Maps API quadtree format
* the url of the directory is specified in the json fields of the overlay object
* (client just pulls the url from json instead of calculating it)
* Two url fields in the json:
* 'tilesUrl' -- points to quadtree form of original image
* 'registeredTilesUrl' -- points to quadtree form of warped image
* not specified unless warping is complete
JSON format for saving everything:
xp, yp in spherical mercator
x, y in image coords
matrix: mapping from image coordinates to spherical mercator coords
{"points":[array of points in [xp,yp,x,y] form],
type: "similarity" or "affine" or "projective",
matrix: [r11,r12,...,r33],
"url":"url to overlay (/overlay/<id>.json)",
"tilesUrl":"url to tiles (/data/geocamTiePoint/tiles/<id>)",
"registeredTilesUrl":"url to warped tiles (/data/geocamTiePoint/registeredTiles/<id>)"
Scheme for warping and tiling images (SWaTI)
T is the transform from original image coords to spherical mercator
Tinv is the inverse of T
tileIndex(zoom, mercatorCoords) gives the x, y tile indices
tileExtent(zoom, x, y) gives extent of tile in spherical mercator coords
mercatorCorners = [T(corner) for corner in originalImageSize]
bounds = BoundingBox()
for corner in mercatorCorners:
maxZoom = calculateMaxZoom()
for zoom in xrange(maxZoom):
bounds = BoundingBox()
for corner in mercatorCorners:
tileCoords = tileIndex(zoom, mercatorCorner)
xmin, ymin = tileIndex(zoom, (bounds.xmin, bounds.ymin))
xmax, ymax = tileIndex(zoom, (bounds.xmax, bounds.ymax))
(be careful about off-by-one on the high end of the bounds)
for x in xrange(xmin, xmax + 1?):
for y in xrange(ymin, ymax + 1?):
corners = tileExtent(zoom, x, y)
imageCorners = Tinv(corners)
tileData = im.transform(imageCorners)
if nonEmpty(tileData):
def calculateMaxZoom():
metersPerPixelX = (bounds.xmax - bounds.xmin) / image.width
metersPerPixelY = (bounds.ymax - bounds.ymin) / image.height
metersPerOIPixel = min(metersPerPixelX, metersPerPixelY)
# metersPerTile = C / 2**zoom
# originalImagePixelsPerTile = metersPerTile / metersPerOIPixel
# originalImagePixelsPerTile = (C / 2**zoom) / metersPerOIPixel
# choose zoom such that originalImagePixelsPerTile <= 256
zoom = ceil(solve(eqn)) + 1?
Something went wrong with that request. Please try again.