-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathprotocol.txt
132 lines (109 loc) · 4.66 KB
/
protocol.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
URLs:
Refer to http://microformats.org/wiki/rest/urls
* /overlay/
* GET:
* returns an index page of all the overlays
* click on entry, takes you to editor
* separate link to new overlay page
* POST:
* n/a, http 405
* /overlay/<id>/delete/
* GET:
* returns confirmation page
* POST:
* deletes objects
* redirects to index
* /overlay/new/
* GET:
* returns an image upload form (html)
* POST:
* 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],
"transform":{
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:
bounds.extend(corner)
maxZoom = calculateMaxZoom()
for zoom in xrange(maxZoom):
bounds = BoundingBox()
for corner in mercatorCorners:
tileCoords = tileIndex(zoom, mercatorCorner)
bounds.extend(tileCoords)
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):
writeTile(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?