/*******************************************************************************
** (C) KEISUKE KONDO
**
** Release Date: March 31, 2018
** Latest Update: June 9, 2021
**
** Version: 1.21
**
** [Contact]
** Email: kondo-keisuke@rieti.go.jp
** URL: https://keisukekondokk.github.io/
*******************************************************************************/
** Version: 1.21
** Added generate option
** Coding improvement for saving memory space
** Version: 1.20
** Bug fix for p-value
** Added swm(knn #) option
** Added "r(W)" in r()
** Alert message for missing observations
** Coding improvement
** Version: 1.10
** Abbreviation of "detail" option changed
**
**
capture program drop moransi
program moransi, sortpreserve rclass
	version 11
	syntax varlist [if] [in], /*
			*/ lat(varname) /*
			*/ lon(varname) /*
			*/ swm(string) /*
			*/ dist(real) /*
			*/ dunit(string) /*
			*/ [ /*
			*/ DMS /*
			*/ APProx /*
			*/ DETail /*
			*/ NOMATsave /*
			*/ GENerate ]

	local vY `varlist'
	local swmtype = substr("`swm'", 1, 3)
	local swmtype_short = substr("`swm'", 1, 1)
	local unit = "`dunit'"
	marksample touse
	markout `touse' `vY' `lon' `lat'

	/*Check Variable*/
	if( strpos("`vY'"," ") > 0 ){
		display as error "Multiple variables are not allowed."
		exit 198
	}

	/*Check Missing Values of Variable*/
	qui: egen ______missing_count_spgen = rowmiss(`vY')
	qui: count if ______missing_count_spgen > 0
	local nummissing = r(N)
	local errormissing = 0
	if(`nummissing' > 0){
		local errormissing = 1
	}
	if( `errormissing' == 1){
		if(`nummissing' == 1){
			display as text "Warning: `nummissing' observation with missing value is dropped."
		}
		if(`nummissing' > 1){
			display as text "Warning: `nummissing' observations with missing value are dropped."
		}
	}
	qui: drop ______missing_count_spgen

	/*Check Latitude Range*/
	qui: sum `lat'
	local max_lat = r(max)
	local min_lat = r(min)
	if( `max_lat' < -90 | `min_lat' < -90 ){
		display as error "lat() must be within -90 to 90."
		exit 198
	}
	if( `max_lat' > 90 | `min_lat' > 90 ){
		display as error "lat() must be within -90 to 90."
		exit 198
	}

	/*Check Longitude Range*/
	qui: sum `lon'
	local max_lon = r(max)
	local min_lon = r(min)
	if( `max_lon' < -180 | `min_lon' < -180 ){
		display as error "lon() must be within -180 to 180."
		exit 198
	}
	if( `max_lon' > 180 | `min_lon' > 180 ){
		display as error "lon() must be within -180 to 180."
		exit 198
	}

	/*Check Spatial Weight Matrix*/
	if( "`swmtype'" != "bin" & "`swmtype'" != "knn" & "`swmtype'" != "exp" & "`swmtype'" != "pow"){
		display as error "swm() must be one of bin, knn, exp, and pow."
		exit 198
	}

	/*Check Distance Decay Parameter of Spatial Weight Matrix*/
	if( "`swmtype'" == "bin" ){
		local dd = . /*not used*/
	}
	else if( "`swmtype'" == "knn" ){
		local dd = real(substr("`swm'", strpos("`swm'", "knn") + length("knn") + 1, .))
		capture confirm integer number `dd'
		if( _rc != 0 ){
			display as error "Parameter {it:k} of swm(knn) must be integer."
			exit 198
		}
		if( `dd' <= 0 ){
			display as error "Parameter {it:k} of swm(knn) must be more than 0."
			exit 198
		}
		if( `dd' > _N ){
			qui: count
			local totalobs = r(N)
			display as error "Parameter {it:k} of swm(knn) must be less than `totalobs' (# of obs)."
			exit 198
		}
		else if( `dd' == . ){
			display as error "Numerical type is expected for distance-decay parameter."
			exit 198
		}
	}
	else if( "`swmtype'" == "exp" ){
		local dd = real(substr("`swm'", strpos("`swm'", "exp") + length("exp") + 1, .))
		if( `dd' <= 0 ){
			display as error "Distance-decay parameter must be more than 0."
			exit 198
		}
		else if( `dd' == . ){
			display as error "Numerical type is expected for distance-decay parameter."
			exit 198
		}
	}
	else if( "`swmtype'" == "pow" ){
		local dd = real(substr("`swm'", strpos("`swm'", "pow") + length("pow") + 1, .))
		if( `dd' <= 0 ){
			display as error "Distance-decay parameter must be more than 0."
			exit 198
		}
		else if( `dd' == . ){
			display as error "Numerical type is expected for distance-decay parameter."
			exit 198
		}
	}

	/*Check Parameter Range*/
	if( `dist' <= 0 ){
		display as error "dist(#) must be more than 0."
		exit 198
	}
	if( "`swmtype'" == "knn" & `dist' > 0 & `dist' != . ){
		local dist = .
		display as text "Warning: dist() is ignored for swm(knn) option."
	}

	/*Check Unit of Distance*/
	if( "`unit'" != "km" & "`unit'" != "mi" ){
		display as error "dunit(unit) must be either km or mi."
		exit 198
	}

	/*DMS or Decimal*/
	local fmdms = 0
	if( "`dms'" != "" ){
		local fmdms = 1
	}

	/*Approximation of Distance*/
	local appdist = 0
	if( "`approx'" != "" ){
		local appdist = 1
	}

	/*Display Details*/
	local dispdetail= 0
	if( "`detail'" != "" ){
		local dispdetail = 1
	}

	/*Distance Matrix Save Option*/
	local matsave = 1
	if( "`nomatsave'" != "" ){
		local matsave = 0
	}

	/*Check before Making Output Variable*/
	local gensplag = 0
	if( "`generate'" != "" ){
		capture confirm new variable splag_`vY'_`swmtype_short', exact
		if( _rc == 0 ) {
			local gensplag = 1
		}
		else if( _rc != 0 ) {
			display as error "Outcome variable for `vY' already exists."
			exit _rc
		}
	}

	/*+++++CALL Mata Program+++++*/
	mata: calcmoransi("`vY'", "`lon'", "`lat'", `fmdms', "`swmtype'", `dist', "`unit'", `dd', `appdist', `dispdetail', `matsave', `gensplag', "`touse'")
	/*+++++END Mata Program+++++*/

	/*Store Results in return*/
	return add

	/*Generate Spatial Lag*/
	if( "`generate'" != "" ){
		/*Add Variable Label*/
		label var splag_`vY'_`swmtype_short' "sptial lag, swm(`swm'), td=`dist', od=`order', row-standadized"

		/*Display Results*/
		display as txt "{bf:splag_`vY'_`swmtype_short'} was generated in the dataset."
	}
end

version 11
mata:
void calcmoransi(vY, lon, lat, fmdms, swmtype, dist, unit, dd, appdist, dispdetail, matsave, gensplag, touse)
{
	/*Make Variable*/
	if( fmdms == "1" ){
		printf("Convert DMS format to Decimal format.")
		convlonlat2decimal(lon, lat, touse, &vlon, &vlat)
	} else {
		st_view(vlon, ., lon, touse)
		st_view(vlat, ., lat, touse)
	}
	latr = ( pi() / 180 ) * vlat;
	lonr = ( pi() / 180 ) * vlon;

	/*Make Variable*/
	st_view(vy, ., vY, touse)
	cN = rows(vlon)

	/*Size of SWM*/
	printf("{txt}Size of spatial weight matrix:{res} %1.0f * %1.0f\n", cN, cN)

	/*Variables*/
	if( matsave == 1 ){
		mD = J(cN, cN, 0)
	}
	mD_L = .
	cN_vD = .
	dist_mean = .
	dist_sd = .
	dist_min = .
	dist_max = 0
	numErrorSwm = 0
	numErrorSwmNoNeighbor = 0

	/*Spatial Lag*/
	vDist = J(1, cN, 0)
	vW = J(1, cN, 0)
	mW = J(cN, cN, 0)
	vsy = ( vy :- mean(vy) ) :/ sqrt( variance(vy) )
	vwy = J(cN, 1, 0)
	vwsy = J(cN, 1, 0)

	/*Make Distance Matrix using Vincenty (1975) */
	if( appdist == 1 ){
		/*Simplified Version of Vincenty Formula*/
		for( i = 1; i <= cN; ++i ){

			/* Display Iteration Process */
			if( i == 1 ){
				printf("{txt}{c TT}{hline 15}{c TT}\n")
			}

			/*Distance between i and j*/
			A = J(1, cN, 1) :* lonr[i]
			B = J(1, cN, 1) :* lonr'
			C = J(1, cN, 1) :* latr[i]
			D = J(1, cN, 1) :* latr'
			difflonr = abs( A - B )
			numer1 = ( cos(D):*sin(difflonr) ):^2
			numer2 = ( cos(C):*sin(D) :- sin(C):*cos(D):*cos(difflonr) ):^2
			numer = sqrt( numer1 :+ numer2 )
			denom = sin(C):*sin(D) :+ cos(C):*cos(D):*cos(difflonr)
			vDist = 6378.137 * atan2( denom, numer )
			/*Missing between i and i*/
			vDist[i] = .

			/*Convert Unit of Distance*/
			if( unit == "mi" ){
				vDist = 0.621371 :* vDist
			}

			/*Store Min and Max Distance*/
			if( min(vDist) < dist_min ){
				dist_min = min(vDist)
			}
			if( max(vDist) > dist_max ){
				dist_max = max(vDist)
			}

			/*Save Distance Matrix*/
			if( matsave == 1 ){
				mD[i,] = vDist
			}

			/*Binary SWM*/
			if( swmtype == "bin" ){
				vW = ( vDist :< dist )
				vDist = .
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW) == 0)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*K-Nearest Neighbor SWM*/
			if( swmtype == "knn" ){
				/*Obtain Threshold Distance for KNN*/
				vDistSorted = sort(vDist', 1)'
				dDistKnn = vDistSorted[dd]
				vW = ( vDist :<= dDistKnn )
				vDist = .
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW :!= 0) > dd)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*Exponential SWM*/
			if( swmtype == "exp" ){
				vW = ( vDist :< dist ) :* exp( - dd :* vDist )
				vDist = .
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*Power SWM*/
			if( swmtype == "pow" ){
				vW = ( vDist :< dist ) :* vDist :^(-dd)
				vDist = .
				/*ERROR CHECK*/
				numErrorSwmNoNeighbor = numErrorSwmNoNeighbor + (rowsum(vW) == 0)
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW :== .) > 1 | rowsum(vW) == 0)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}

			/*Spatial Lagged Variable*/
			mW[i,] = vW
			vwy[i,] = vW * vy
			vwsy[i,] = vW * vsy

			/* Display Iteration Process */
			if( i == trunc(cN/10) ){
				printf("{txt}{c |}Completed:  10%%{c |}\n")
			}
			else if( i == 2*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  20%%{c |}\n")
			}
			else if( i == 3*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  30%%{c |}\n")
			}
			else if( i == 4*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  40%%{c |}\n")
			}
			else if( i == trunc(cN/2) ){
				printf("{txt}{c |}Completed:  50%%{c |}\n")
			}
			else if( i == trunc(cN/2) + trunc(cN/10) ){
				printf("{txt}{c |}Completed:  60%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 2*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  70%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 3*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  80%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 4*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  90%%{c |}\n")
			}
			else if( i == cN ){
				printf("{txt}{c |}Completed: 100%%{c |}\n")
				printf("{txt}{c BT}{hline 15}{c BT}\n")
			}
		}

		/*REPORT ERROR CHECK*/
		if( swmtype == "bin" ){
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has no neighbors. Confirm dist() option.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have no neighbors. Confirm dist() option.\n", numErrorSwm)
				}
			}
		}
		if( swmtype == "knn" ){
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has multiple k-nearest neighbors.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have multiple k-nearest neighbors.\n", numErrorSwm)
				}
			}
		}
		if( swmtype == "pow" ){
			if(numErrorSwmNoNeighbor > 0){
				if(numErrorSwmNoNeighbor == 1){
					printf("{txt}Warning: %1.0f observation has no neighbors. Confirm dist() option.\n", numErrorSwmNoNeighbor)
				}
				if(numErrorSwmNoNeighbor > 1){
					printf("{txt}Warning: %1.0f observations have no neighbors. Confirm dist() option.\n", numErrorSwmNoNeighbor)
				}
			}
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has missing value.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have missing values.\n", numErrorSwm)
				}
			}
		}
	}
	else if( appdist == 0 ){
		/*Vincenty Formula*/

		/*Variables*/
		a = 6378.137
		b = 6356.752314245
		f = (a-b)/a
		eps = 1e-12
		maxIt = 1e+5

		/*LOOP for Vincenty Formula*/
		for( i = 1; i <= cN; ++i ){

			/* Display Iteration Process */
			if( i == 1 ){
				printf("{txt}{c TT}{hline 15}{c TT}\n")
			}

			Alon = J(1, cN, 1) :* lonr[i]
			Blon = J(1, cN, 1) :* lonr'
			Clat = J(1, cN, 1) :* latr[i]
			Dlat = J(1, cN, 1) :* latr'
			U1 = atan( (1-f) :* tan(Clat) )
			U2 = atan( (1-f) :* tan(Dlat) )
			L = abs( Alon :- Blon )
			lam = L
			l1_lam = lam
			cnt = 0
			do{
				numer1 = ( cos(U2) :* sin(lam) ):^2
				numer2 = ( cos(U1) :* sin(U2) :- sin(U1) :* cos(U2) :* cos(lam) ):^2
				numer = sqrt( numer1 :+ numer2 )
				denom = sin(U1) :* sin(U2) :+ cos(U1) :* cos(U2) :* cos(lam)
				sig = atan2( denom, numer )
				sinalp = (cos(U1) :* cos(U2) :* sin(lam)) :/ sin(sig)
				cos2alp = 1 :- sinalp:^2
				cos2sigm = cos(sig) :- ( 2 :* sin(U1) :* sin(U2) ) :/ cos2alp
				C = f:/16 :* cos2alp :* ( 4 :+ f :* (4 :- 3 :* cos2alp) )
				lam = L :+ (1:-C) :* f :* sinalp :* ( sig :+ C :* sin(sig) :* ( cos2sigm :+ C :* cos(sig) :*(-1 :+ 2 :* cos2sigm:^2) ) )
				cri = abs( max(lam :- l1_lam) )
				l1_lam = lam;
				if( cnt++ > maxIt ){
					printf("{err}Convergence not achieved in Vincenty formula \n")
					printf("{err}Add approx option to avoid convergence error \n")
					exit(error(430))
				}
			}while( cri > eps )

			/*After Iteration*/
			u2 = cos2alp :* ( (a^2 - b^2) / b^2 )
			A = 1 :+ (u2 :/ 16384) :* ( 4096 :+ u2 :* (-768 :+ u2 :* (320 :- 175 :* u2)) )
			B = u2 :/ 1024 :* (256 :+ u2 :* (-128 :+ u2 :* (74 :- 47 :*u2)) )
			dsig = B:*sin(sig) :* ( cos2sigm :+ 0.25:*B:*( cos(sig):*(-1:+2:*cos2sigm:^2) :- 1:/6:*B:*cos2sigm:*(-3:+4:*sin(sig):^2):*(-3:+4:*cos2sigm) ) )
			vDist = b :* A :* (sig :- dsig)

			/*Distance = 0*/
			if(missing(vDist) > 1){
				_editmissing(vDist, 0)
			}

			/*Missing between i and i*/
			vDist[i] = .

			/*Convert Unit of Distance*/
			if( unit == "mi" ){
				vDist = 0.621371 :* vDist
			}

			/*Store Min and Max Distance*/
			if( min(vDist) < dist_min ){
				dist_min = min(vDist)
			}
			if( max(vDist) > dist_max ){
				dist_max = max(vDist)
			}

			/*Save Distance Matrix*/
			if( matsave == 1 ){
				mD[i,] = vDist
			}

			/*Binary SWM*/
			if( swmtype == "bin" ){
				vW = ( vDist :< dist )
				vDist = .
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW) == 0)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*K-Nearest Neighbor SWM*/
			if( swmtype == "knn" ){
				/*Obtain Threshold Distance for KNN*/
				vDistSorted = sort(vDist', 1)'
				dDistKnn = vDistSorted[dd]
				vW = ( vDist :<= dDistKnn )
				vDist = .
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW :!= 0) > dd)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*Exponential SWM*/
			if( swmtype == "exp" ){
				vW = ( vDist :< dist ) :* exp( - dd :* vDist )
				vDist = .
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}
			/*Power SWM*/
			if( swmtype == "pow" ){
				vW = ( vDist :< dist ) :* vDist :^(-dd)
				vDist = .
				/*ERROR CHECK*/
				numErrorSwmNoNeighbor = numErrorSwmNoNeighbor + (rowsum(vW) == 0)
				/*ERROR CHECK*/
				numErrorSwm = numErrorSwm + (rowsum(vW :== .) > 1 | rowsum(vW) == 0)
				/*Diagonal Element*/
				vW[i] = 0
				vW = vW :/ rowsum(vW)
			}

			/*Spatial Lagged Variable*/
			mW[i,] = vW
			vwy[i,] = vW * vy
			vwsy[i,] = vW * vsy

			/* Display Iteration Process */
			if( i == trunc(cN/10) ){
				printf("{txt}{c |}Completed:  10%%{c |}\n")
			}
			else if( i == 2*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  20%%{c |}\n")
			}
			else if( i == 3*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  30%%{c |}\n")
			}
			else if( i == 4*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  40%%{c |}\n")
			}
			else if( i == trunc(cN/2) ){
				printf("{txt}{c |}Completed:  50%%{c |}\n")
			}
			else if( i == trunc(cN/2) + trunc(cN/10) ){
				printf("{txt}{c |}Completed:  60%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 2*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  70%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 3*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  80%%{c |}\n")
			}
			else if( i == trunc(cN/2) + 4*trunc(cN/10) ){
				printf("{txt}{c |}Completed:  90%%{c |}\n")
			}
			else if( i == cN ){
				printf("{txt}{c |}Completed: 100%%{c |}\n")
				printf("{txt}{c BT}{hline 15}{c BT}\n")
			}
		}

		/*REPORT ERROR CHECK*/
		if( swmtype == "bin" ){
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has no neighbors. Confirm dist() option.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have no neighbors. Confirm dist() option.\n", numErrorSwm)
				}
			}
		}
		if( swmtype == "knn" ){
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has multiple k-nearest neighbors.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have multiple k-nearest neighbors.\n", numErrorSwm)
				}
			}
		}
		if( swmtype == "pow" ){
			if(numErrorSwmNoNeighbor > 0){
				if(numErrorSwmNoNeighbor == 1){
					printf("{txt}Warning: %1.0f observation has no neighbors. Confirm dist() option.\n", numErrorSwmNoNeighbor)
				}
				if(numErrorSwmNoNeighbor > 1){
					printf("{txt}Warning: %1.0f observations have no neighbors. Confirm dist() option.\n", numErrorSwmNoNeighbor)
				}
			}
			if(numErrorSwm > 0){
				if(numErrorSwm == 1){
					printf("{txt}Warning: %1.0f observation has missing value.\n", numErrorSwm)
				}
				if(numErrorSwm > 1){
					printf("{txt}Warning: %1.0f observations have missing values.\n", numErrorSwm)
				}
			}
		}
	}

	/*Moran's I from Definition*/
	dS0 = cN
	dI = (vsy'vwsy) / (vsy'vsy)

	/*Calculate Expectation and Variance of Moran's I*/
	vS1 = J(cN, 1, 0)
	vS2 = J(cN, 1, 0)
	for( i = 1; i <= cN; ++i ){
		vS1[i] = rowsum( (mW[i,] :+ mW[,i]'):^2 )
		vS2[i] = ( rowsum(mW[i,] :+ mW[,i]'):^2 )
	}
	dS1 = 0.5 * colsum( vS1 )
	dS2 = 1.0 * colsum( vS2 )
	vS1 = .
	vS2 = .
	dD = cN * colsum( vsy:^4 ) / (colsum(vsy:^2))^2
	dC = (cN-1)*(cN-2)*(cN-3)*dS0^2
	dB = dD * ( (cN^2-cN)*dS1 - 2*cN*dS2 + 6*dS0^2 )
	dA = cN * ( (cN^2-3*cN+3)*dS1 - cN*dS2 + 3*dS0^2 )

	/*Calculate Z-value of Moran's I*/
	if(dI == .){
		dEI = .
		dEI2 = .
		dVI = .
		dSEI = .
		dZI = .
		dPI = .
	}
	else {
		dEI = - 1 / ( cN - 1 )
		dEI2 = (dA - dB) / dC
		dVI = dEI2 - (dEI)^2
		dSEI = sqrt(dVI)
		dZI = (dI - dEI) / dSEI
		dPI = 2 * ( 1 - normal(abs(dZI)) )
	}

	/*Summary Statistics of Distance Matrix*/
	if( matsave == 1 ){
		mD_L = lowertriangle(mD)
		mD = .
		vD = select(vech(mD_L), vech(mD_L):>0)
		cN_vD = rows(vD)
		dist_mean = mean(vD)
		dist_sd = sqrt(variance(vD))
		vD = .
	}
	else {
		mW = .
		mD_L = .
		cN_vD = .
		dist_mean = .
		dist_sd = .
	}

	/*Display Summary Statistics of Distances*/
	printf("\n")
	if( appdist == 1 ){
		if( unit == "km" ){
			printf("{txt}Distance by simplified version of Vincenty formula (unit: km)\n")
		}
		else if( unit == "mi" ){
			printf("{txt}Distance by simplified version of Vincenty formula (unit: mi)\n")
		}
	}
	else if( appdist == 0 ){
		if( unit == "km" ){
			printf("{txt}Distance by Vincenty formula (unit: km)\n")
		}
		else if( unit == "mi" ){
			printf("{txt}Distance by Vincenty formula (unit: mi)\n")
		}
	}
	if( dispdetail == 1 ){
		printf("{txt}{hline 21}{c TT}{hline 63} \n")
		printf("{txt}{space 20} {c |}{space 8}Obs.{space 8}Mean{space 9}S.D.{space 9}Min.{space 9}Max.\n")
		printf("{txt}{hline 21}{c +}{hline 63} \n")
		printf("{txt}{space 12}Distance {c |}{res}  %10.0f  %10.3f   %10.3f   %10.3f   %10.3f\n",
					cN_vD, dist_mean, dist_sd, dist_min, dist_max )
		printf("{txt}{hline 21}{c BT}{hline 63} \n")
		if( unit == "km" ){
			printf("{txt}Distance threshold (unit: km):{res} %10.0f\n", dist)
		}
		else if( unit == "mi" ){
			printf("{txt}Distance threshold (unit: mi):{res} %10.0f\n", dist)
		}
		printf("{txt}{hline 85}\n")
	}

	/*For Long Variable Name*/
	sY = abbrev(vY, 20)

	/*Results of Moran's I*/
	printf("\n")
	printf("{txt}Moran's I Statistic {space 40} Number of Obs = {res}%8.0f\n",cN)
	printf("{txt}{hline 21}{c TT}{hline 63}\n")
	printf("{txt}{space 12}Variable {c |}  Moran's I{space 9}E(I){space 8}SE(I){space 9}Z(I){space 6}p-value\n")
	printf("{hline 21}{c +}{hline 63}\n")
	printf("{txt}%20s {c |} {res}%10.5f   %10.5f   %10.5f   %10.5f   %10.5f\n", sY, dI, dEI, dSEI, dZI, dPI )
	printf("{txt}{hline 21}{c BT}{hline 63}\n")
	printf("{txt}Null Hypothesis: Spatial Randomization\n")

	/*Return Results in Mata to Stata*/
	if( gensplag == 1 ){
		st_store(., st_addvar("float", "splag"+"_"+vY+"_"+substr(swmtype, 1, 1)), st_local("touse"), vwy)
	}

	/*rreturn command in Stata*/
	st_rclear()
	st_numscalar("r(dist_max)", dist_max)
	st_numscalar("r(dist_min)", dist_min)
	st_numscalar("r(dist_sd)", dist_sd)
	st_numscalar("r(dist_mean)", dist_mean)
	st_numscalar("r(dd)", dd)
	st_numscalar("r(td)", dist)
	st_numscalar("r(N)", cN)
	st_numscalar("r(pI)", dPI)
	st_numscalar("r(zI)", dZI)
	st_numscalar("r(seI)", dSEI)
	st_numscalar("r(EI)", dEI)
	st_numscalar("r(I)", dI)
	st_matrix("r(W)", mW)
	st_matrix("r(D)", mD_L)
	if( swmtype == "bin" ){
		st_global("r(swm)", "binary")
	}
	else if( swmtype == "exp" ){
		st_global("r(swm)", "exponential")
	}
	else if( swmtype == "pow" ){
		st_global("r(swm)", "power")
	}
	if( unit == "km" ){
		st_global("r(dunit)", "km")
	}
	else if( unit == "mi" ){
		st_global("r(dunit)", "mi")
	}
	if( appdist == 1 ){
		st_global("r(dist_type)", "approximation")
	}
	else if( appdist == 0 ){
		st_global("r(dist_type)", "exact")
	}
	st_global("r(varname)", vY)
	st_global("r(cmd)", "moransi")
}
end

/*## MATA ## Convert DMS format to Decimal Format*/
version 11
mata:
void convlonlat2decimal(lat, lon, touse, vlat, vlon)
{
	st_view(vlat_, ., lat, touse)
	st_view(vlon_, ., lon, touse)
	(*vlat) = floor(vlat_) :+ (floor((vlat_:-floor(vlat_)):*100):/60) :+ (floor((vlat_:*100:-floor(vlat_:*100)):*100):/3600)
	(*vlon) = floor(vlon_) :+ (floor((vlon_:-floor(vlon_)):*100):/60) :+ (floor((vlon_:*100:-floor(vlon_:*100)):*100):/3600)
}
end