mirror of
https://gitlab.com/NvlblNm/wayfarer.git
synced 2024-09-18 02:24:58 +08:00
Merge branch 'tntnnbltn-voting-proximity' into 'master'
Level 17 cell voting proximity See merge request NvlblNm/wayfarer!19
This commit is contained in:
commit
f6ee61bc9f
@ -31,6 +31,7 @@ function wrapper(pluginInfo) {
|
||||
let plottedtitles = {}
|
||||
let plottedsubmitrange = {}
|
||||
let plottedinteractrange = {}
|
||||
let plottedcells = {}
|
||||
|
||||
// Define the layers created by the plugin, one for each marker status
|
||||
const mapLayers = {
|
||||
@ -90,6 +91,7 @@ function wrapper(pluginInfo) {
|
||||
showTitles: true,
|
||||
showRadius: false,
|
||||
showInteractionRadius: false,
|
||||
showVotingProximity: false,
|
||||
scriptURL: '',
|
||||
disableDraggingMarkers: false,
|
||||
enableCoordinatesEdit: true,
|
||||
@ -174,6 +176,7 @@ function wrapper(pluginInfo) {
|
||||
addMarkerToLayer(candidate)
|
||||
addTitleToLayer(candidate)
|
||||
addCircleToLayer(candidate)
|
||||
addVotingProximity(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,12 +274,60 @@ function wrapper(pluginInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
function addVotingProximity(candidate) {
|
||||
if (settings.showVotingProximity && candidate.status === 'voting') {
|
||||
const cell = S2.S2Cell.FromLatLng(
|
||||
{ lat: candidate.lat, lng: candidate.lng },
|
||||
17
|
||||
)
|
||||
const surrounding = cell.getSurrounding()
|
||||
surrounding.push(cell)
|
||||
|
||||
for (let i = 0; i < surrounding.length; i++) {
|
||||
const cellId = surrounding[i].toString()
|
||||
if (!plottedcells[cellId]) {
|
||||
plottedcells[cellId] = { candidateIds: [], polygon: null }
|
||||
const vertexes = surrounding[i].getCornerLatLngs()
|
||||
const polygon = L.polygon(vertexes, {
|
||||
color: 'black',
|
||||
opacity: 0.5,
|
||||
fillColor: 'orange',
|
||||
fillOpacity: 0.3
|
||||
})
|
||||
plottedcells[cellId].polygon = polygon
|
||||
polygon.addTo(map)
|
||||
}
|
||||
if (
|
||||
plottedcells[cellId].candidateIds.indexOf(candidate.id) ===
|
||||
-1
|
||||
) {
|
||||
plottedcells[cellId].candidateIds.push(candidate.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeExistingVotingProximity(guid) {
|
||||
Object.entries(plottedcells).forEach(
|
||||
([cellId, { candidateIds, polygon }]) => {
|
||||
plottedcells[cellId].candidateIds = candidateIds.filter(
|
||||
(id) => id !== guid
|
||||
)
|
||||
if (plottedcells[cellId].candidateIds.length === 0) {
|
||||
map.removeLayer(polygon)
|
||||
delete plottedcells[cellId]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function removeExistingMarker(guid) {
|
||||
const existingMarker = plottedmarkers[guid]
|
||||
if (existingMarker !== undefined) {
|
||||
existingMarker.layer.removeLayer(existingMarker.marker)
|
||||
removeExistingTitle(guid)
|
||||
removeExistingCircle(guid)
|
||||
removeExistingVotingProximity(guid)
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,12 +372,16 @@ function wrapper(pluginInfo) {
|
||||
|
||||
function clearAllLayers() {
|
||||
Object.values(mapLayers).forEach((data) => data.layer.clearLayers())
|
||||
Object.values(plottedcells).forEach((data) =>
|
||||
map.removeLayer(data.polygon)
|
||||
)
|
||||
|
||||
/* clear marker storage */
|
||||
plottedmarkers = {}
|
||||
plottedtitles = {}
|
||||
plottedsubmitrange = {}
|
||||
plottedinteractrange = {}
|
||||
plottedcells = {}
|
||||
}
|
||||
|
||||
function drawMarkers() {
|
||||
@ -567,6 +622,7 @@ function wrapper(pluginInfo) {
|
||||
<p><input type="checkbox" id="chkShowTitles"><label for="chkShowTitles">Show titles</label></p>
|
||||
<p><input type="checkbox" id="chkShowRadius"><label for="chkShowRadius">Show submit radius</label></p>
|
||||
<p><input type="checkbox" id="chkShowInteractRadius"><label for="chkShowInteractRadius">Show interaction radius</label></p>
|
||||
<p><input type="checkbox" id="chkShowVotingProximity"><label for="chkShowVotingProximity">Show voting proximity</label></p>
|
||||
<p><input type="checkbox" id="chkDisableDraggingMarkers"><label for="chkDisableDraggingMarkers">Disable Dragging Markers</label></p>
|
||||
<p><input type="checkbox" id="chkEnableCoordinatesEdit"><label for="chkEnableCoordinatesEdit">Enable Coordinates Edit</label></p>
|
||||
<p><input type="checkbox" id="chkEnableImagePreview"><label for="chkEnableImagePreview">Enable Image Preview</label></p>
|
||||
@ -659,6 +715,15 @@ function wrapper(pluginInfo) {
|
||||
saveSettings()
|
||||
drawMarkers()
|
||||
})
|
||||
const chkShowVotingProximity = div.querySelector(
|
||||
'#chkShowVotingProximity'
|
||||
)
|
||||
chkShowVotingProximity.checked = settings.showVotingProximity
|
||||
chkShowVotingProximity.addEventListener('change', (e) => {
|
||||
settings.showVotingProximity = chkShowVotingProximity.checked
|
||||
saveSettings()
|
||||
drawMarkers()
|
||||
})
|
||||
const chkDisableDraggingMarkers = div.querySelector(
|
||||
'#chkDisableDraggingMarkers'
|
||||
)
|
||||
@ -908,6 +973,274 @@ function wrapper(pluginInfo) {
|
||||
})
|
||||
}
|
||||
|
||||
/** S2 Geometry functions
|
||||
|
||||
S2 extracted from Regions Plugin
|
||||
https:static.iitc.me/build/release/plugins/regions.user.js
|
||||
|
||||
*/
|
||||
|
||||
const d2r = Math.PI / 180.0
|
||||
const r2d = 180.0 / Math.PI
|
||||
|
||||
const S2 = {}
|
||||
|
||||
function LatLngToXYZ(latLng) {
|
||||
const phi = latLng.lat * d2r
|
||||
const theta = latLng.lng * d2r
|
||||
const cosphi = Math.cos(phi)
|
||||
|
||||
return [
|
||||
Math.cos(theta) * cosphi,
|
||||
Math.sin(theta) * cosphi,
|
||||
Math.sin(phi)
|
||||
]
|
||||
}
|
||||
|
||||
function XYZToLatLng(xyz) {
|
||||
const lat = Math.atan2(
|
||||
xyz[2],
|
||||
Math.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1])
|
||||
)
|
||||
const lng = Math.atan2(xyz[1], xyz[0])
|
||||
|
||||
return { lat: lat * r2d, lng: lng * r2d }
|
||||
}
|
||||
|
||||
function largestAbsComponent(xyz) {
|
||||
const temp = [Math.abs(xyz[0]), Math.abs(xyz[1]), Math.abs(xyz[2])]
|
||||
|
||||
if (temp[0] > temp[1]) {
|
||||
if (temp[0] > temp[2]) {
|
||||
return 0
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
if (temp[1] > temp[2]) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 2
|
||||
}
|
||||
|
||||
function faceXYZToUV(face, xyz) {
|
||||
let u, v
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
u = xyz[1] / xyz[0]
|
||||
v = xyz[2] / xyz[0]
|
||||
break
|
||||
case 1:
|
||||
u = -xyz[0] / xyz[1]
|
||||
v = xyz[2] / xyz[1]
|
||||
break
|
||||
case 2:
|
||||
u = -xyz[0] / xyz[2]
|
||||
v = -xyz[1] / xyz[2]
|
||||
break
|
||||
case 3:
|
||||
u = xyz[2] / xyz[0]
|
||||
v = xyz[1] / xyz[0]
|
||||
break
|
||||
case 4:
|
||||
u = xyz[2] / xyz[1]
|
||||
v = -xyz[0] / xyz[1]
|
||||
break
|
||||
case 5:
|
||||
u = -xyz[1] / xyz[2]
|
||||
v = -xyz[0] / xyz[2]
|
||||
break
|
||||
default:
|
||||
throw { error: 'Invalid face' }
|
||||
}
|
||||
|
||||
return [u, v]
|
||||
}
|
||||
|
||||
function XYZToFaceUV(xyz) {
|
||||
let face = largestAbsComponent(xyz)
|
||||
|
||||
if (xyz[face] < 0) {
|
||||
face += 3
|
||||
}
|
||||
|
||||
const uv = faceXYZToUV(face, xyz)
|
||||
|
||||
return [face, uv]
|
||||
}
|
||||
|
||||
function FaceUVToXYZ(face, uv) {
|
||||
const u = uv[0]
|
||||
const v = uv[1]
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
return [1, u, v]
|
||||
case 1:
|
||||
return [-u, 1, v]
|
||||
case 2:
|
||||
return [-u, -v, 1]
|
||||
case 3:
|
||||
return [-1, -v, -u]
|
||||
case 4:
|
||||
return [v, -1, -u]
|
||||
case 5:
|
||||
return [v, u, -1]
|
||||
default:
|
||||
throw { error: 'Invalid face' }
|
||||
}
|
||||
}
|
||||
|
||||
function STToUV(st) {
|
||||
const singleSTtoUV = function (st) {
|
||||
if (st >= 0.5) {
|
||||
return (1 / 3.0) * (4 * st * st - 1)
|
||||
}
|
||||
return (1 / 3.0) * (1 - 4 * (1 - st) * (1 - st))
|
||||
}
|
||||
|
||||
return [singleSTtoUV(st[0]), singleSTtoUV(st[1])]
|
||||
}
|
||||
|
||||
function UVToST(uv) {
|
||||
const singleUVtoST = function (uv) {
|
||||
if (uv >= 0) {
|
||||
return 0.5 * Math.sqrt(1 + 3 * uv)
|
||||
}
|
||||
return 1 - 0.5 * Math.sqrt(1 - 3 * uv)
|
||||
}
|
||||
|
||||
return [singleUVtoST(uv[0]), singleUVtoST(uv[1])]
|
||||
}
|
||||
|
||||
function STToIJ(st, order) {
|
||||
const maxSize = 1 << order
|
||||
|
||||
const singleSTtoIJ = function (st) {
|
||||
const ij = Math.floor(st * maxSize)
|
||||
return Math.max(0, Math.min(maxSize - 1, ij))
|
||||
}
|
||||
|
||||
return [singleSTtoIJ(st[0]), singleSTtoIJ(st[1])]
|
||||
}
|
||||
|
||||
function IJToST(ij, order, offsets) {
|
||||
const maxSize = 1 << order
|
||||
|
||||
return [(ij[0] + offsets[0]) / maxSize, (ij[1] + offsets[1]) / maxSize]
|
||||
}
|
||||
|
||||
// S2Cell class
|
||||
S2.S2Cell = function () {}
|
||||
|
||||
// static method to construct
|
||||
S2.S2Cell.FromLatLng = function (latLng, level) {
|
||||
const xyz = LatLngToXYZ(latLng)
|
||||
const faceuv = XYZToFaceUV(xyz)
|
||||
const st = UVToST(faceuv[1])
|
||||
const ij = STToIJ(st, level)
|
||||
|
||||
return S2.S2Cell.FromFaceIJ(faceuv[0], ij, level)
|
||||
}
|
||||
|
||||
S2.S2Cell.FromFaceIJ = function (face, ij, level) {
|
||||
const cell = new S2.S2Cell()
|
||||
cell.face = face
|
||||
cell.ij = ij
|
||||
cell.level = level
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
S2.S2Cell.prototype.toString = function () {
|
||||
return (
|
||||
'F' +
|
||||
this.face +
|
||||
'ij[' +
|
||||
this.ij[0] +
|
||||
',' +
|
||||
this.ij[1] +
|
||||
']@' +
|
||||
this.level
|
||||
)
|
||||
}
|
||||
|
||||
S2.S2Cell.prototype.getLatLng = function () {
|
||||
const st = IJToST(this.ij, this.level, [0.5, 0.5])
|
||||
const uv = STToUV(st)
|
||||
const xyz = FaceUVToXYZ(this.face, uv)
|
||||
|
||||
return XYZToLatLng(xyz)
|
||||
}
|
||||
|
||||
S2.S2Cell.prototype.getCornerLatLngs = function () {
|
||||
const offsets = [
|
||||
[0.0, 0.0],
|
||||
[0.0, 1.0],
|
||||
[1.0, 1.0],
|
||||
[1.0, 0.0]
|
||||
]
|
||||
|
||||
return offsets.map((offset) => {
|
||||
const st = IJToST(this.ij, this.level, offset)
|
||||
const uv = STToUV(st)
|
||||
const xyz = FaceUVToXYZ(this.face, uv)
|
||||
|
||||
return XYZToLatLng(xyz)
|
||||
})
|
||||
}
|
||||
|
||||
S2.S2Cell.prototype.getSurrounding = function (deltas) {
|
||||
const fromFaceIJWrap = function (face, ij, level) {
|
||||
const maxSize = 1 << level
|
||||
if (
|
||||
ij[0] >= 0 &&
|
||||
ij[1] >= 0 &&
|
||||
ij[0] < maxSize &&
|
||||
ij[1] < maxSize
|
||||
) {
|
||||
// no wrapping out of bounds
|
||||
return S2.S2Cell.FromFaceIJ(face, ij, level)
|
||||
}
|
||||
|
||||
// the new i,j are out of range.
|
||||
// with the assumption that they're only a little past the borders we can just take the points as
|
||||
// just beyond the cube face, project to XYZ, then re-create FaceUV from the XYZ vector
|
||||
let st = IJToST(ij, level, [0.5, 0.5])
|
||||
let uv = STToUV(st)
|
||||
const xyz = FaceUVToXYZ(face, uv)
|
||||
const faceuv = XYZToFaceUV(xyz)
|
||||
face = faceuv[0]
|
||||
uv = faceuv[1]
|
||||
st = UVToST(uv)
|
||||
ij = STToIJ(st, level)
|
||||
return S2.S2Cell.FromFaceIJ(face, ij, level)
|
||||
}
|
||||
|
||||
const face = this.face
|
||||
const i = this.ij[0]
|
||||
const j = this.ij[1]
|
||||
const level = this.level
|
||||
|
||||
if (!deltas) {
|
||||
deltas = [
|
||||
{ a: -1, b: 0 },
|
||||
{ a: 0, b: -1 },
|
||||
{ a: 1, b: 0 },
|
||||
{ a: 0, b: 1 },
|
||||
{ a: -1, b: -1 },
|
||||
{ a: 1, b: 1 },
|
||||
{ a: -1, b: 1 },
|
||||
{ a: 1, b: -1 }
|
||||
]
|
||||
}
|
||||
return deltas.map(function (values) {
|
||||
return fromFaceIJWrap(face, [i + values.a, j + values.b], level)
|
||||
})
|
||||
}
|
||||
|
||||
// PLUGIN END //////////////////////////////////////////////////////////
|
||||
|
||||
setup.info = pluginInfo // add the script info data to the function as a property
|
||||
|
Loading…
Reference in New Issue
Block a user