-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
172 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
NewSourcePage = require "../pages/NewSourcePage" | ||
|
||
# Menu that displays when a right-click or long-press is detected | ||
module.exports = class ContextMenu | ||
constructor: (map, ctx) -> | ||
@map = map | ||
|
||
# Listen for event | ||
@map.on 'contextmenu', (e) => | ||
# Ignore if not logged in | ||
if not NewSourcePage.canOpen(ctx) | ||
return | ||
|
||
# Get location | ||
geo = { | ||
type: "Point" | ||
coordinates: [e.latlng.lng, e.latlng.lat] | ||
} | ||
|
||
# Create popup html | ||
contents = $('<div><button class="btn">Create Water Source</button></div>') | ||
|
||
# Create popup | ||
popup = L.popup({ closeButton: false }) | ||
.setLatLng(e.latlng) | ||
.setContent(contents.get(0)) | ||
.openOn(map) | ||
|
||
contents.find('button').on 'click', -> | ||
map.closePopup(popup) | ||
ctx.pager.openPage(NewSourcePage, { geo: geo }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
LocationFinder = require '../LocationFinder' | ||
|
||
|
||
# Displays current location as a blue dot | ||
module.exports = class LocationDisplay | ||
# Setup display, optionally zooming to current location | ||
constructor: (map, zoomTo) -> | ||
@map = map | ||
@zoomTo = zoomTo | ||
|
||
@locationFinder = new LocationFinder() | ||
@locationFinder.on('found', @locationFound).on('error', @locationError) | ||
@locationFinder.startWatch() | ||
|
||
stop: -> | ||
@locationFinder.stopWatch() | ||
|
||
locationError: (e) => | ||
if @zoomTo | ||
@map.fitWorld() | ||
@zoomTo = false | ||
alert("Unable to determine location") | ||
|
||
locationFound: (e) => | ||
radius = e.coords.accuracy | ||
latlng = new L.LatLng(e.coords.latitude, e.coords.longitude) | ||
|
||
# Set position once | ||
if @zoomTo | ||
zoom = 15 | ||
@map.setView(latlng, zoom) | ||
@zoomTo = false | ||
|
||
# Radius larger than 1km means no location worth displaying | ||
if radius > 1000 | ||
return | ||
|
||
# Setup marker and circle | ||
if not @meMarker | ||
icon = L.icon(iconUrl: "img/my_location.png", iconSize: [22, 22]) | ||
@meMarker = L.marker(latlng, icon:icon).addTo(@map) | ||
@meCircle = L.circle(latlng, radius) | ||
@meCircle.addTo(@map) | ||
else | ||
@meMarker.setLatLng(latlng) | ||
@meCircle.setLatLng(latlng).setRadius(radius) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
ItemTracker = require "../ItemTracker" | ||
GeoJSON = require '../GeoJSON' | ||
SourcePage = require "../pages/SourcePage" | ||
|
||
# Displays water sources on map. Call updateMarkers to refresh. Creates popups | ||
# with source details. | ||
module.exports = class SourceDisplay | ||
constructor: (map, db, pager) -> | ||
@map = map | ||
@db = db | ||
@pager = pager | ||
@itemTracker = new ItemTracker() | ||
|
||
@sourceMarkers = {} | ||
|
||
@icon = new L.icon | ||
iconUrl: 'img/DropMarker.png' | ||
iconRetinaUrl: 'img/[email protected]' | ||
iconSize: [27, 41], | ||
iconAnchor: [13, 41] | ||
popupAnchor: [-3, -41] | ||
|
||
updateMarkers: => | ||
# Get bounds padded | ||
bounds = @map.getBounds() | ||
if not bounds.isValid() | ||
return | ||
if bounds.getWest() == bounds.getEast() | ||
return | ||
if bounds.getNorth() == bounds.getSouth() | ||
return | ||
|
||
bounds = bounds.pad(0.33) | ||
boundsGeoJSON = GeoJSON.latLngBoundsToGeoJSON(bounds) | ||
|
||
# Spherical Polygons must fit within a hemisphere. | ||
# Any geometry specified with GeoJSON to $geoIntersects or $geoWithin queries, must fit within a single hemisphere. | ||
# MongoDB interprets geometries larger than half of the sphere as queries for the smaller of the complementary geometries. | ||
# So... don't bother intersection if large | ||
if (boundsGeoJSON.coordinates[0][2][0] - boundsGeoJSON.coordinates[0][0][0]) >= 180 | ||
selector = {} | ||
else if (boundsGeoJSON.coordinates[0][2][1] - boundsGeoJSON.coordinates[0][0][1]) >= 180 | ||
selector = {} | ||
else | ||
selector = { geo: { $geoIntersects: { $geometry: boundsGeoJSON } } } | ||
|
||
# Query sources with projection. Use remote mode so no caching occurs | ||
queryOptions = | ||
sort: ["_id"] | ||
limit: 100 | ||
mode: "remote" | ||
fields: { name: 1, code: 1, geo: 1 } | ||
|
||
@db.sources.find(selector, queryOptions).fetch (sources) => | ||
# Find out which to add/remove | ||
[adds, removes] = @itemTracker.update(sources) | ||
|
||
# Remove old markers | ||
for remove in removes | ||
@removeSourceMarker(remove) | ||
for add in adds | ||
@addSourceMarker(add) | ||
|
||
addSourceMarker: (source) -> | ||
if source.geo? | ||
latlng = new L.LatLng(source.geo.coordinates[1], source.geo.coordinates[0]) | ||
marker = new L.Marker(latlng, {icon:@icon}) | ||
|
||
# Create popup | ||
html = _.template(''' | ||
<div> | ||
Id: <b><%=source.code%></b><br> | ||
Name: <b><%=source.name%></b><br> | ||
<button class="btn">Open</button> | ||
</div>''', | ||
{ source: source }) | ||
|
||
content = $(html) | ||
content.find("button").on 'click', => | ||
@pager.openPage(SourcePage, {_id: source._id}) | ||
|
||
marker.bindPopup(content.get(0)) | ||
|
||
@sourceMarkers[source._id] = marker | ||
marker.addTo(@map) | ||
|
||
removeSourceMarker: (source) -> | ||
if _.has(@sourceMarkers, source._id) | ||
@map.removeLayer(@sourceMarkers[source._id]) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,8 @@ | ||
Page = require "../Page" | ||
SourcePage = require "./SourcePage" | ||
NewSourcePage = require "./NewSourcePage" | ||
ItemTracker = require "../ItemTracker" | ||
LocationFinder = require '../LocationFinder' | ||
GeoJSON = require '../GeoJSON' | ||
|
||
SourceDisplay = require '../map/SourcesLayer' # TODO | ||
LocationDisplay = require '../map/LocationDisplay' | ||
ContextMenu = require '../map/ContextMenu' | ||
|
||
# Map of water sources. Options include: | ||
# initialGeo: Geometry to zoom to. Point only supported. | ||
|
@@ -65,167 +64,5 @@ setupMapTiles = -> | |
mapquestAttrib = 'Data, imagery and map information provided by <a href="http://open.mapquest.co.uk" target="_blank">MapQuest</a>, <a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> and contributors.' | ||
return new L.TileLayer(mapquestUrl, {maxZoom: 18, attribution: mapquestAttrib, subdomains: subDomains}) | ||
|
||
# Displays water sources on map. Call updateMarkers to refresh. Creates popups | ||
# with source details. | ||
class SourceDisplay | ||
constructor: (map, db, pager) -> | ||
@map = map | ||
@db = db | ||
@pager = pager | ||
@itemTracker = new ItemTracker() | ||
|
||
@sourceMarkers = {} | ||
|
||
@icon = new L.icon | ||
iconUrl: 'img/DropMarker.png' | ||
iconRetinaUrl: 'img/[email protected]' | ||
iconSize: [27, 41], | ||
iconAnchor: [13, 41] | ||
popupAnchor: [-3, -41] | ||
|
||
updateMarkers: => | ||
# Get bounds padded | ||
bounds = @map.getBounds() | ||
if not bounds.isValid() | ||
return | ||
if bounds.getWest() == bounds.getEast() | ||
return | ||
if bounds.getNorth() == bounds.getSouth() | ||
return | ||
|
||
bounds = bounds.pad(0.33) | ||
boundsGeoJSON = GeoJSON.latLngBoundsToGeoJSON(bounds) | ||
|
||
# Spherical Polygons must fit within a hemisphere. | ||
# Any geometry specified with GeoJSON to $geoIntersects or $geoWithin queries, must fit within a single hemisphere. | ||
# MongoDB interprets geometries larger than half of the sphere as queries for the smaller of the complementary geometries. | ||
# So... don't bother intersection if large | ||
if (boundsGeoJSON.coordinates[0][2][0] - boundsGeoJSON.coordinates[0][0][0]) >= 180 | ||
selector = {} | ||
else if (boundsGeoJSON.coordinates[0][2][1] - boundsGeoJSON.coordinates[0][0][1]) >= 180 | ||
selector = {} | ||
else | ||
selector = { geo: { $geoIntersects: { $geometry: boundsGeoJSON } } } | ||
|
||
# Query sources with projection. Use remote mode so no caching occurs | ||
queryOptions = | ||
sort: ["_id"] | ||
limit: 100 | ||
mode: "remote" | ||
fields: { name: 1, code: 1, geo: 1 } | ||
|
||
@db.sources.find(selector, queryOptions).fetch (sources) => | ||
# Find out which to add/remove | ||
[adds, removes] = @itemTracker.update(sources) | ||
|
||
# Remove old markers | ||
for remove in removes | ||
@removeSourceMarker(remove) | ||
for add in adds | ||
@addSourceMarker(add) | ||
|
||
addSourceMarker: (source) -> | ||
if source.geo? | ||
latlng = new L.LatLng(source.geo.coordinates[1], source.geo.coordinates[0]) | ||
marker = new L.Marker(latlng, {icon:@icon}) | ||
|
||
# Create popup | ||
html = _.template(''' | ||
<div> | ||
Id: <b><%=source.code%></b><br> | ||
Name: <b><%=source.name%></b><br> | ||
<button class="btn">Open</button> | ||
</div>''', | ||
{ source: source }) | ||
|
||
content = $(html) | ||
content.find("button").on 'click', => | ||
@pager.openPage(SourcePage, {_id: source._id}) | ||
|
||
marker.bindPopup(content.get(0)) | ||
|
||
@sourceMarkers[source._id] = marker | ||
marker.addTo(@map) | ||
|
||
removeSourceMarker: (source) -> | ||
if _.has(@sourceMarkers, source._id) | ||
@map.removeLayer(@sourceMarkers[source._id]) | ||
|
||
|
||
# Displays current location as a blue dot | ||
class LocationDisplay | ||
# Setup display, optionally zooming to current location | ||
constructor: (map, zoomTo) -> | ||
@map = map | ||
@zoomTo = zoomTo | ||
|
||
@locationFinder = new LocationFinder() | ||
@locationFinder.on('found', @locationFound).on('error', @locationError) | ||
@locationFinder.startWatch() | ||
|
||
stop: -> | ||
@locationFinder.stopWatch() | ||
|
||
locationError: (e) => | ||
if @zoomTo | ||
@map.fitWorld() | ||
@zoomTo = false | ||
alert("Unable to determine location") | ||
|
||
locationFound: (e) => | ||
radius = e.coords.accuracy | ||
latlng = new L.LatLng(e.coords.latitude, e.coords.longitude) | ||
|
||
# Set position once | ||
if @zoomTo | ||
zoom = 15 | ||
@map.setView(latlng, zoom) | ||
@zoomTo = false | ||
|
||
# Radius larger than 1km means no location worth displaying | ||
if radius > 1000 | ||
return | ||
|
||
# Setup marker and circle | ||
if not @meMarker | ||
icon = L.icon(iconUrl: "img/my_location.png", iconSize: [22, 22]) | ||
@meMarker = L.marker(latlng, icon:icon).addTo(@map) | ||
@meCircle = L.circle(latlng, radius) | ||
@meCircle.addTo(@map) | ||
else | ||
@meMarker.setLatLng(latlng) | ||
@meCircle.setLatLng(latlng).setRadius(radius) | ||
|
||
|
||
# Menu that displays when a right-click or long-press is detected | ||
class ContextMenu | ||
constructor: (map, ctx) -> | ||
@map = map | ||
|
||
# Listen for event | ||
@map.on 'contextmenu', (e) => | ||
# Ignore if not logged in | ||
if not NewSourcePage.canOpen(ctx) | ||
return | ||
|
||
# Get location | ||
geo = { | ||
type: "Point" | ||
coordinates: [e.latlng.lng, e.latlng.lat] | ||
} | ||
|
||
# Create popup html | ||
contents = $('<div><button class="btn">Create Water Source</button></div>') | ||
|
||
# Create popup | ||
popup = L.popup({ closeButton: false }) | ||
.setLatLng(e.latlng) | ||
.setContent(contents.get(0)) | ||
.openOn(map) | ||
|
||
contents.find('button').on 'click', -> | ||
map.closePopup(popup) | ||
ctx.pager.openPage(NewSourcePage, { geo: geo }) | ||
|
||
|
||
module.exports = SourceMapPage |