This 2021 development note records a map control I built for a Scriptcase application. The task was to edit the location of a record on a map instead of relying only on typed longitude and latitude values.
The editing flow
The control opened with the record’s existing coordinates. Moving a marker updated the coordinate fields; changing the fields moved the marker. A reset action returned to the starting position, while confirmation passed the selected coordinates back to the parent form.
The stored longitude and latitude used decimal degrees in WGS 84. In the projects described here, database spatial queries also associated a point with geographic areas such as an island or administrative district.
Video record
Original implementation
The following SQL, HTML and JavaScript are retained from the original article. They depend on its table names, form fields, Scriptcase template and map integration. They are a development record, not a tested current installation package.
Database trigger example
BEGIN
IF (st_x(NEW.shape)<>st_x(OLD.shape)) OR (st_y(NEW.shape)<>st_y(OLD.shape)) THEN
NEW.periferia_id = (SELECT periferies_new.objectid
from sde.periferies_new, sde.bats_locations
where st_contains (periferies_new.shape, bats_locations.shape) = 't'
and bats_locations.objectid = NEW.objectid);
NEW.nomos_id = (SELECT nomoi.objectid
from sde.nomoi, sde.bats_locations
where st_contains (nomoi.shape, bats_locations.shape) = 't'
and bats_locations.objectid = NEW.objectid);
NEW.island_id = (SELECT coast_withnames.objectid
from sde.coast_withnames, sde.bats_locations
where st_contains (coast_withnames.shape, bats_locations.shape) = 't'
and bats_locations.objectid = NEW.objectid);
END IF;
NEW.update_date = now();
RETURN NEW;
ENDControl template
<html>
<head>
<!--SC_PAGE_CHARSET-->
<!--SC_JS_LIB-->
<link rel="stylesheet" type="text/css" href="{SC_LIB_CURRENT}/css/bootstrap/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="{SC_LIB_CURRENT}/css/controlpoints.css" />
<script type="text/javascript" src="{SC_LIB_CURRENT}/js/bootstrap/bootstrap.min.js"></script>
<script type="text/javascript" src="{SC_LIB_CURRENT}/js/controlpoints.js"></script>
<title><!--SC_PAGE_TITLE--></title>
</head>
<body>
<div class="row">
<div class="col-12 col-sm-6 col-md-8">
<div id="map">
<div style="width: 100%; height: 100%; " id="map"></div>
<div id="content-window"> </div>
</div>
</div>
<div class="col-6 col-md-4">
<form {SC_FORM_ATTR}>
<!--SC_FORM_HIDDEN-->
<!--SC_FIELD_LABEL_my_field-->
<br />
<input {SC_FIELD_INFO_idlocality} class="form-control {SC_FIELD_CLASS}" type="text" />
<div class="coords">
<div class="coord">
<span class="label">Longitude</span>
<input {SC_FIELD_INFO_londd} class="form-control {SC_FIELD_CLASS}" type="text" />
</div>
<div class="coord">
<span class="label">Latitude</span>
<input {SC_FIELD_INFO_latdd} class="form-control {SC_FIELD_CLASS}" type="text" />
</div>
<div class="coord submitbutton">
<input type="submit" {SC_FORM_SUBMIT_INFO} />
</div>
</div>
</form>
<button onclick="resetPoint()">Reset Position</button>
<button id="closewindow">Cancel / Exit</button>
<div id="location-info">
<h2>Existing location information</h2>
<div class="location-info idlocation">
<span class="label-info">GIS ID</span>
<input {SC_FIELD_INFO_gisid} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">District</span>
<input {SC_FIELD_INFO_periferia} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">Perfecture</span>
<input {SC_FIELD_INFO_nomos} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">Island name</span>
<input {SC_FIELD_INFO_island_name} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">Area</span>
<input {SC_FIELD_INFO_area} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">Location name</span>
<input {SC_FIELD_INFO_location} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
<div class="location-info">
<span class="label-info">Update date / time</span>
<input {SC_FIELD_INFO_update_date} class="form-control {SC_FIELD_CLASS}" type="text" readonly="readonly" />
</div>
</div>
</div>
</div>
<!-- This contains the hidden content for inline calls -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_HERE&callback=initMap">
</script>
</body>
</html>Map-control script
var marker;
// Initial coordinates from the parent form
var thelon;
var thelat;
// New coordinates given from the map or disrectly from the input coordinate fields
var newlon;
var newlat;
window.map = undefined; // global variable
function scDisplayUserError(errorMessage) {
alert("ERRORrn" + errorMessage.replace(/<br />/gi, "n"));
}
function scDisplayUserDebug(debugMessage) {
alert("DEBUGrn" + debugMessage.replace(/<br />/gi, "n"));
}function scDisplayUserMessage(userMessage) {
alert("MESSAGErn" + userMessage.replace(/<br />/gi, "n"));
}
// This will reset the coordinates to the initial values. This is an alternative to reexamine the initial position of the point without closing and reopening the control
function resetPoint() {
// Set the values of the coordinates from the parent form
thelon=parseFloat(parent.document.getElementById("id_sc_field_londd_manual").value);
thelat=parseFloat(parent.document.getElementById("id_sc_field_latdd_manual").value);
// Assign the values to the respective input fields of the iframe control form
document.getElementById("id_sc_field_londd").value=thelon;
document.getElementById("id_sc_field_latdd").value=thelat;
// Reset the center of the map and the marker position
const center = new google.maps.LatLng(thelat, thelon);
window.map.panTo(center);
marker.setPosition( new google.maps.LatLng( thelat, thelon ) );
}
function initMap() {
// Set initial coordinates values from parent form
thelon = parseFloat(parent.document.getElementById("id_sc_field_londd_manual").value);
thelat = parseFloat(parent.document.getElementById("id_sc_field_latdd_manual").value);
document.getElementById('id_sc_field_londd').value = thelon;
document.getElementById('id_sc_field_latdd').value = thelat;
var myLatLng = {lat: thelat, lng: thelon};
// Starting parameters for the map by setting a default zoom and centering at the location of the marker
window.map = new google.maps.Map(document.getElementById('map'), {
zoom: 11,
center: myLatLng
});
pointLatLng = new google.maps.LatLng({lat: thelat, lng: thelon});
marker = new google.maps.Marker({
position: myLatLng,
map: map,
position: pointLatLng,
draggable: true,
title: 'Custom point'
});
<!-- Move the marker by dragging it -->
marker.addListener('dragend', function(evt) {
console.log('Lat: ' + evt.latLng.lat() + ' Lon: ' + evt.latLng.lng());
newlon=parseFloat((Math.round(evt.latLng.lng() * 10000000) / 10000000).toFixed(7));
newlat=parseFloat((Math.round(evt.latLng.lat() * 10000000) / 10000000).toFixed(7));
document.getElementById('id_sc_field_londd').value = newlon;
document.getElementById('id_sc_field_latdd').value = newlat;
});
<!-- On single click move the marker -->
google.maps.event.addListener(map, 'click', function(evt) {
newlon=parseFloat((Math.round(evt.latLng.lng() * 10000000) / 10000000).toFixed(7));
newlat=parseFloat((Math.round(evt.latLng.lat() * 10000000) / 10000000).toFixed(7));
const center = new google.maps.LatLng(newlat, newlon);
window.map.panTo(center);
marker.setPosition( new google.maps.LatLng( newlat, newlon ) );
document.getElementById('id_sc_field_londd').value = newlon;
document.getElementById('id_sc_field_latdd').value = newlat;
});
}
$(document).ready(function () {
$('#closewindow').click(function (){
$('#TB_overlay', window.parent.document).remove();
$('#TB_window', window.parent.document).remove();
});
});
function exitModal() {
parent.document.getElementById('id_sc_field_londd_manual').value = newlon;
parent.document.getElementById('id_sc_field_latdd_manual').value = newlat;
$('#TB_overlay', window.parent.document).remove();
$('#TB_window', window.parent.document).remove();
}
// Ajax executed function on coordinate fields change
function coordinatesChanged(thelon,thelat) {
newlon=thelon;
newlat=thelat;
document.getElementById("id_sc_field_londd").value=thelon;
document.getElementById("id_sc_field_latdd").value=thelat;
const center = new google.maps.LatLng(thelat, thelon);
window.map.panTo(center);
marker.setPosition( new google.maps.LatLng( thelat, thelon ) );
}
The wider aim was to keep the record and its geographic position consistent. Related examples are collected in the project portfolio.

